0cebc0dfd3e42ebb75c2bbc0d96e0c46a1d64bdf
[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         Roo.log("TOOLTIP_PLACEMENT");
32769         Roo.log(this.bindEl.attr('tooltip-placement'));
32770         if(this.bindEl.attr('tooltip-placement')) {
32771             placement = this.bindEl.attr('tooltip-placement');
32772         }
32773             
32774         var autoToken = /\s?auto?\s?/i;
32775         var autoPlace = autoToken.test(placement);
32776         if (autoPlace) {
32777             placement = placement.replace(autoToken, '') || 'top';
32778         }
32779         
32780         //this.el.detach()
32781         //this.el.setXY([0,0]);
32782         this.el.show();
32783         //this.el.dom.style.display='block';
32784         
32785         //this.el.appendTo(on_el);
32786         
32787         var p = this.getPosition();
32788         var box = this.el.getBox();
32789         
32790         if (autoPlace) {
32791             // fixme..
32792         }
32793         
32794         var align = this.alignment[placement];
32795         
32796         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
32797         
32798         if(placement == 'top' || placement == 'bottom'){
32799             if(xy[0] < 0){
32800                 placement = 'right';
32801             }
32802             
32803             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
32804                 placement = 'left';
32805             }
32806             
32807             var scroll = Roo.select('body', true).first().getScroll();
32808             
32809             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
32810                 placement = 'top';
32811             }
32812             
32813             align = this.alignment[placement];
32814             
32815             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
32816             
32817         }
32818         
32819         var elems = document.getElementsByTagName('div');
32820         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
32821         for (var i = 0; i < elems.length; i++) {
32822           var zindex = Number.parseInt(
32823                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
32824                 10
32825           );
32826           if (zindex > highest) {
32827             highest = zindex;
32828           }
32829         }
32830         
32831         
32832         
32833         this.el.dom.style.zIndex = highest;
32834         
32835         this.el.alignTo(this.bindEl, align[0],align[1]);
32836         //var arrow = this.el.select('.arrow',true).first();
32837         //arrow.set(align[2], 
32838         
32839         this.el.addClass(placement);
32840         this.el.addClass("bs-tooltip-"+ placement);
32841         
32842         this.el.addClass('in fade show');
32843         
32844         this.hoverState = null;
32845         
32846         if (this.el.hasClass('fade')) {
32847             // fade it?
32848         }
32849         
32850         
32851         
32852         
32853         
32854     },
32855     hide : function()
32856     {
32857          
32858         if (!this.el) {
32859             return;
32860         }
32861         //this.el.setXY([0,0]);
32862         this.el.removeClass(this.bindEl.attr('tooltip-class'));
32863         this.el.removeClass(['show', 'in']);
32864         //this.el.hide();
32865         
32866     }
32867     
32868 });
32869  
32870
32871  /*
32872  * - LGPL
32873  *
32874  * Location Picker
32875  * 
32876  */
32877
32878 /**
32879  * @class Roo.bootstrap.LocationPicker
32880  * @extends Roo.bootstrap.Component
32881  * Bootstrap LocationPicker class
32882  * @cfg {Number} latitude Position when init default 0
32883  * @cfg {Number} longitude Position when init default 0
32884  * @cfg {Number} zoom default 15
32885  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
32886  * @cfg {Boolean} mapTypeControl default false
32887  * @cfg {Boolean} disableDoubleClickZoom default false
32888  * @cfg {Boolean} scrollwheel default true
32889  * @cfg {Boolean} streetViewControl default false
32890  * @cfg {Number} radius default 0
32891  * @cfg {String} locationName
32892  * @cfg {Boolean} draggable default true
32893  * @cfg {Boolean} enableAutocomplete default false
32894  * @cfg {Boolean} enableReverseGeocode default true
32895  * @cfg {String} markerTitle
32896  * 
32897  * @constructor
32898  * Create a new LocationPicker
32899  * @param {Object} config The config object
32900  */
32901
32902
32903 Roo.bootstrap.LocationPicker = function(config){
32904     
32905     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
32906     
32907     this.addEvents({
32908         /**
32909          * @event initial
32910          * Fires when the picker initialized.
32911          * @param {Roo.bootstrap.LocationPicker} this
32912          * @param {Google Location} location
32913          */
32914         initial : true,
32915         /**
32916          * @event positionchanged
32917          * Fires when the picker position changed.
32918          * @param {Roo.bootstrap.LocationPicker} this
32919          * @param {Google Location} location
32920          */
32921         positionchanged : true,
32922         /**
32923          * @event resize
32924          * Fires when the map resize.
32925          * @param {Roo.bootstrap.LocationPicker} this
32926          */
32927         resize : true,
32928         /**
32929          * @event show
32930          * Fires when the map show.
32931          * @param {Roo.bootstrap.LocationPicker} this
32932          */
32933         show : true,
32934         /**
32935          * @event hide
32936          * Fires when the map hide.
32937          * @param {Roo.bootstrap.LocationPicker} this
32938          */
32939         hide : true,
32940         /**
32941          * @event mapClick
32942          * Fires when click the map.
32943          * @param {Roo.bootstrap.LocationPicker} this
32944          * @param {Map event} e
32945          */
32946         mapClick : true,
32947         /**
32948          * @event mapRightClick
32949          * Fires when right click the map.
32950          * @param {Roo.bootstrap.LocationPicker} this
32951          * @param {Map event} e
32952          */
32953         mapRightClick : true,
32954         /**
32955          * @event markerClick
32956          * Fires when click the marker.
32957          * @param {Roo.bootstrap.LocationPicker} this
32958          * @param {Map event} e
32959          */
32960         markerClick : true,
32961         /**
32962          * @event markerRightClick
32963          * Fires when right click the marker.
32964          * @param {Roo.bootstrap.LocationPicker} this
32965          * @param {Map event} e
32966          */
32967         markerRightClick : true,
32968         /**
32969          * @event OverlayViewDraw
32970          * Fires when OverlayView Draw
32971          * @param {Roo.bootstrap.LocationPicker} this
32972          */
32973         OverlayViewDraw : true,
32974         /**
32975          * @event OverlayViewOnAdd
32976          * Fires when OverlayView Draw
32977          * @param {Roo.bootstrap.LocationPicker} this
32978          */
32979         OverlayViewOnAdd : true,
32980         /**
32981          * @event OverlayViewOnRemove
32982          * Fires when OverlayView Draw
32983          * @param {Roo.bootstrap.LocationPicker} this
32984          */
32985         OverlayViewOnRemove : true,
32986         /**
32987          * @event OverlayViewShow
32988          * Fires when OverlayView Draw
32989          * @param {Roo.bootstrap.LocationPicker} this
32990          * @param {Pixel} cpx
32991          */
32992         OverlayViewShow : true,
32993         /**
32994          * @event OverlayViewHide
32995          * Fires when OverlayView Draw
32996          * @param {Roo.bootstrap.LocationPicker} this
32997          */
32998         OverlayViewHide : true,
32999         /**
33000          * @event loadexception
33001          * Fires when load google lib failed.
33002          * @param {Roo.bootstrap.LocationPicker} this
33003          */
33004         loadexception : true
33005     });
33006         
33007 };
33008
33009 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
33010     
33011     gMapContext: false,
33012     
33013     latitude: 0,
33014     longitude: 0,
33015     zoom: 15,
33016     mapTypeId: false,
33017     mapTypeControl: false,
33018     disableDoubleClickZoom: false,
33019     scrollwheel: true,
33020     streetViewControl: false,
33021     radius: 0,
33022     locationName: '',
33023     draggable: true,
33024     enableAutocomplete: false,
33025     enableReverseGeocode: true,
33026     markerTitle: '',
33027     
33028     getAutoCreate: function()
33029     {
33030
33031         var cfg = {
33032             tag: 'div',
33033             cls: 'roo-location-picker'
33034         };
33035         
33036         return cfg
33037     },
33038     
33039     initEvents: function(ct, position)
33040     {       
33041         if(!this.el.getWidth() || this.isApplied()){
33042             return;
33043         }
33044         
33045         this.el.setVisibilityMode(Roo.Element.DISPLAY);
33046         
33047         this.initial();
33048     },
33049     
33050     initial: function()
33051     {
33052         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
33053             this.fireEvent('loadexception', this);
33054             return;
33055         }
33056         
33057         if(!this.mapTypeId){
33058             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
33059         }
33060         
33061         this.gMapContext = this.GMapContext();
33062         
33063         this.initOverlayView();
33064         
33065         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
33066         
33067         var _this = this;
33068                 
33069         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
33070             _this.setPosition(_this.gMapContext.marker.position);
33071         });
33072         
33073         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
33074             _this.fireEvent('mapClick', this, event);
33075             
33076         });
33077
33078         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
33079             _this.fireEvent('mapRightClick', this, event);
33080             
33081         });
33082         
33083         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
33084             _this.fireEvent('markerClick', this, event);
33085             
33086         });
33087
33088         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
33089             _this.fireEvent('markerRightClick', this, event);
33090             
33091         });
33092         
33093         this.setPosition(this.gMapContext.location);
33094         
33095         this.fireEvent('initial', this, this.gMapContext.location);
33096     },
33097     
33098     initOverlayView: function()
33099     {
33100         var _this = this;
33101         
33102         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
33103             
33104             draw: function()
33105             {
33106                 _this.fireEvent('OverlayViewDraw', _this);
33107             },
33108             
33109             onAdd: function()
33110             {
33111                 _this.fireEvent('OverlayViewOnAdd', _this);
33112             },
33113             
33114             onRemove: function()
33115             {
33116                 _this.fireEvent('OverlayViewOnRemove', _this);
33117             },
33118             
33119             show: function(cpx)
33120             {
33121                 _this.fireEvent('OverlayViewShow', _this, cpx);
33122             },
33123             
33124             hide: function()
33125             {
33126                 _this.fireEvent('OverlayViewHide', _this);
33127             }
33128             
33129         });
33130     },
33131     
33132     fromLatLngToContainerPixel: function(event)
33133     {
33134         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
33135     },
33136     
33137     isApplied: function() 
33138     {
33139         return this.getGmapContext() == false ? false : true;
33140     },
33141     
33142     getGmapContext: function() 
33143     {
33144         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
33145     },
33146     
33147     GMapContext: function() 
33148     {
33149         var position = new google.maps.LatLng(this.latitude, this.longitude);
33150         
33151         var _map = new google.maps.Map(this.el.dom, {
33152             center: position,
33153             zoom: this.zoom,
33154             mapTypeId: this.mapTypeId,
33155             mapTypeControl: this.mapTypeControl,
33156             disableDoubleClickZoom: this.disableDoubleClickZoom,
33157             scrollwheel: this.scrollwheel,
33158             streetViewControl: this.streetViewControl,
33159             locationName: this.locationName,
33160             draggable: this.draggable,
33161             enableAutocomplete: this.enableAutocomplete,
33162             enableReverseGeocode: this.enableReverseGeocode
33163         });
33164         
33165         var _marker = new google.maps.Marker({
33166             position: position,
33167             map: _map,
33168             title: this.markerTitle,
33169             draggable: this.draggable
33170         });
33171         
33172         return {
33173             map: _map,
33174             marker: _marker,
33175             circle: null,
33176             location: position,
33177             radius: this.radius,
33178             locationName: this.locationName,
33179             addressComponents: {
33180                 formatted_address: null,
33181                 addressLine1: null,
33182                 addressLine2: null,
33183                 streetName: null,
33184                 streetNumber: null,
33185                 city: null,
33186                 district: null,
33187                 state: null,
33188                 stateOrProvince: null
33189             },
33190             settings: this,
33191             domContainer: this.el.dom,
33192             geodecoder: new google.maps.Geocoder()
33193         };
33194     },
33195     
33196     drawCircle: function(center, radius, options) 
33197     {
33198         if (this.gMapContext.circle != null) {
33199             this.gMapContext.circle.setMap(null);
33200         }
33201         if (radius > 0) {
33202             radius *= 1;
33203             options = Roo.apply({}, options, {
33204                 strokeColor: "#0000FF",
33205                 strokeOpacity: .35,
33206                 strokeWeight: 2,
33207                 fillColor: "#0000FF",
33208                 fillOpacity: .2
33209             });
33210             
33211             options.map = this.gMapContext.map;
33212             options.radius = radius;
33213             options.center = center;
33214             this.gMapContext.circle = new google.maps.Circle(options);
33215             return this.gMapContext.circle;
33216         }
33217         
33218         return null;
33219     },
33220     
33221     setPosition: function(location) 
33222     {
33223         this.gMapContext.location = location;
33224         this.gMapContext.marker.setPosition(location);
33225         this.gMapContext.map.panTo(location);
33226         this.drawCircle(location, this.gMapContext.radius, {});
33227         
33228         var _this = this;
33229         
33230         if (this.gMapContext.settings.enableReverseGeocode) {
33231             this.gMapContext.geodecoder.geocode({
33232                 latLng: this.gMapContext.location
33233             }, function(results, status) {
33234                 
33235                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
33236                     _this.gMapContext.locationName = results[0].formatted_address;
33237                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
33238                     
33239                     _this.fireEvent('positionchanged', this, location);
33240                 }
33241             });
33242             
33243             return;
33244         }
33245         
33246         this.fireEvent('positionchanged', this, location);
33247     },
33248     
33249     resize: function()
33250     {
33251         google.maps.event.trigger(this.gMapContext.map, "resize");
33252         
33253         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
33254         
33255         this.fireEvent('resize', this);
33256     },
33257     
33258     setPositionByLatLng: function(latitude, longitude)
33259     {
33260         this.setPosition(new google.maps.LatLng(latitude, longitude));
33261     },
33262     
33263     getCurrentPosition: function() 
33264     {
33265         return {
33266             latitude: this.gMapContext.location.lat(),
33267             longitude: this.gMapContext.location.lng()
33268         };
33269     },
33270     
33271     getAddressName: function() 
33272     {
33273         return this.gMapContext.locationName;
33274     },
33275     
33276     getAddressComponents: function() 
33277     {
33278         return this.gMapContext.addressComponents;
33279     },
33280     
33281     address_component_from_google_geocode: function(address_components) 
33282     {
33283         var result = {};
33284         
33285         for (var i = 0; i < address_components.length; i++) {
33286             var component = address_components[i];
33287             if (component.types.indexOf("postal_code") >= 0) {
33288                 result.postalCode = component.short_name;
33289             } else if (component.types.indexOf("street_number") >= 0) {
33290                 result.streetNumber = component.short_name;
33291             } else if (component.types.indexOf("route") >= 0) {
33292                 result.streetName = component.short_name;
33293             } else if (component.types.indexOf("neighborhood") >= 0) {
33294                 result.city = component.short_name;
33295             } else if (component.types.indexOf("locality") >= 0) {
33296                 result.city = component.short_name;
33297             } else if (component.types.indexOf("sublocality") >= 0) {
33298                 result.district = component.short_name;
33299             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
33300                 result.stateOrProvince = component.short_name;
33301             } else if (component.types.indexOf("country") >= 0) {
33302                 result.country = component.short_name;
33303             }
33304         }
33305         
33306         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
33307         result.addressLine2 = "";
33308         return result;
33309     },
33310     
33311     setZoomLevel: function(zoom)
33312     {
33313         this.gMapContext.map.setZoom(zoom);
33314     },
33315     
33316     show: function()
33317     {
33318         if(!this.el){
33319             return;
33320         }
33321         
33322         this.el.show();
33323         
33324         this.resize();
33325         
33326         this.fireEvent('show', this);
33327     },
33328     
33329     hide: function()
33330     {
33331         if(!this.el){
33332             return;
33333         }
33334         
33335         this.el.hide();
33336         
33337         this.fireEvent('hide', this);
33338     }
33339     
33340 });
33341
33342 Roo.apply(Roo.bootstrap.LocationPicker, {
33343     
33344     OverlayView : function(map, options)
33345     {
33346         options = options || {};
33347         
33348         this.setMap(map);
33349     }
33350     
33351     
33352 });/**
33353  * @class Roo.bootstrap.Alert
33354  * @extends Roo.bootstrap.Component
33355  * Bootstrap Alert class - shows an alert area box
33356  * eg
33357  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
33358   Enter a valid email address
33359 </div>
33360  * @licence LGPL
33361  * @cfg {String} title The title of alert
33362  * @cfg {String} html The content of alert
33363  * @cfg {String} weight (success|info|warning|danger) Weight of the message
33364  * @cfg {String} fa font-awesomeicon
33365  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
33366  * @cfg {Boolean} close true to show a x closer
33367  * 
33368  * 
33369  * @constructor
33370  * Create a new alert
33371  * @param {Object} config The config object
33372  */
33373
33374
33375 Roo.bootstrap.Alert = function(config){
33376     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
33377     
33378 };
33379
33380 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
33381     
33382     title: '',
33383     html: '',
33384     weight: false,
33385     fa: false,
33386     faicon: false, // BC
33387     close : false,
33388     
33389     
33390     getAutoCreate : function()
33391     {
33392         
33393         var cfg = {
33394             tag : 'div',
33395             cls : 'alert',
33396             cn : [
33397                 {
33398                     tag: 'button',
33399                     type :  "button",
33400                     cls: "close",
33401                     html : '×',
33402                     style : this.close ? '' : 'display:none'
33403                 },
33404                 {
33405                     tag : 'i',
33406                     cls : 'roo-alert-icon'
33407                     
33408                 },
33409                 {
33410                     tag : 'b',
33411                     cls : 'roo-alert-title',
33412                     html : this.title
33413                 },
33414                 {
33415                     tag : 'span',
33416                     cls : 'roo-alert-text',
33417                     html : this.html
33418                 }
33419             ]
33420         };
33421         
33422         if(this.faicon){
33423             cfg.cn[0].cls += ' fa ' + this.faicon;
33424         }
33425         if(this.fa){
33426             cfg.cn[0].cls += ' fa ' + this.fa;
33427         }
33428         
33429         if(this.weight){
33430             cfg.cls += ' alert-' + this.weight;
33431         }
33432         
33433         return cfg;
33434     },
33435     
33436     initEvents: function() 
33437     {
33438         this.el.setVisibilityMode(Roo.Element.DISPLAY);
33439         this.titleEl =  this.el.select('.roo-alert-title',true).first();
33440         this.iconEl = this.el.select('.roo-alert-icon',true).first();
33441         this.htmlEl = this.el.select('.roo-alert-text',true).first();
33442         if (this.seconds > 0) {
33443             this.hide.defer(this.seconds, this);
33444         }
33445     },
33446     /**
33447      * Set the Title Message HTML
33448      * @param {String} html
33449      */
33450     setTitle : function(str)
33451     {
33452         this.titleEl.dom.innerHTML = str;
33453     },
33454      
33455      /**
33456      * Set the Body Message HTML
33457      * @param {String} html
33458      */
33459     setHtml : function(str)
33460     {
33461         this.htmlEl.dom.innerHTML = str;
33462     },
33463     /**
33464      * Set the Weight of the alert
33465      * @param {String} (success|info|warning|danger) weight
33466      */
33467     
33468     setWeight : function(weight)
33469     {
33470         if(this.weight){
33471             this.el.removeClass('alert-' + this.weight);
33472         }
33473         
33474         this.weight = weight;
33475         
33476         this.el.addClass('alert-' + this.weight);
33477     },
33478       /**
33479      * Set the Icon of the alert
33480      * @param {String} see fontawsome names (name without the 'fa-' bit)
33481      */
33482     setIcon : function(icon)
33483     {
33484         if(this.faicon){
33485             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
33486         }
33487         
33488         this.faicon = icon;
33489         
33490         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
33491     },
33492     /**
33493      * Hide the Alert
33494      */
33495     hide: function() 
33496     {
33497         this.el.hide();   
33498     },
33499     /**
33500      * Show the Alert
33501      */
33502     show: function() 
33503     {  
33504         this.el.show();   
33505     }
33506     
33507 });
33508
33509  
33510 /*
33511 * Licence: LGPL
33512 */
33513
33514 /**
33515  * @class Roo.bootstrap.UploadCropbox
33516  * @extends Roo.bootstrap.Component
33517  * Bootstrap UploadCropbox class
33518  * @cfg {String} emptyText show when image has been loaded
33519  * @cfg {String} rotateNotify show when image too small to rotate
33520  * @cfg {Number} errorTimeout default 3000
33521  * @cfg {Number} minWidth default 300
33522  * @cfg {Number} minHeight default 300
33523  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
33524  * @cfg {Boolean} isDocument (true|false) default false
33525  * @cfg {String} url action url
33526  * @cfg {String} paramName default 'imageUpload'
33527  * @cfg {String} method default POST
33528  * @cfg {Boolean} loadMask (true|false) default true
33529  * @cfg {Boolean} loadingText default 'Loading...'
33530  * 
33531  * @constructor
33532  * Create a new UploadCropbox
33533  * @param {Object} config The config object
33534  */
33535
33536 Roo.bootstrap.UploadCropbox = function(config){
33537     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
33538     
33539     this.addEvents({
33540         /**
33541          * @event beforeselectfile
33542          * Fire before select file
33543          * @param {Roo.bootstrap.UploadCropbox} this
33544          */
33545         "beforeselectfile" : true,
33546         /**
33547          * @event initial
33548          * Fire after initEvent
33549          * @param {Roo.bootstrap.UploadCropbox} this
33550          */
33551         "initial" : true,
33552         /**
33553          * @event crop
33554          * Fire after initEvent
33555          * @param {Roo.bootstrap.UploadCropbox} this
33556          * @param {String} data
33557          */
33558         "crop" : true,
33559         /**
33560          * @event prepare
33561          * Fire when preparing the file data
33562          * @param {Roo.bootstrap.UploadCropbox} this
33563          * @param {Object} file
33564          */
33565         "prepare" : true,
33566         /**
33567          * @event exception
33568          * Fire when get exception
33569          * @param {Roo.bootstrap.UploadCropbox} this
33570          * @param {XMLHttpRequest} xhr
33571          */
33572         "exception" : true,
33573         /**
33574          * @event beforeloadcanvas
33575          * Fire before load the canvas
33576          * @param {Roo.bootstrap.UploadCropbox} this
33577          * @param {String} src
33578          */
33579         "beforeloadcanvas" : true,
33580         /**
33581          * @event trash
33582          * Fire when trash image
33583          * @param {Roo.bootstrap.UploadCropbox} this
33584          */
33585         "trash" : true,
33586         /**
33587          * @event download
33588          * Fire when download the image
33589          * @param {Roo.bootstrap.UploadCropbox} this
33590          */
33591         "download" : true,
33592         /**
33593          * @event footerbuttonclick
33594          * Fire when footerbuttonclick
33595          * @param {Roo.bootstrap.UploadCropbox} this
33596          * @param {String} type
33597          */
33598         "footerbuttonclick" : true,
33599         /**
33600          * @event resize
33601          * Fire when resize
33602          * @param {Roo.bootstrap.UploadCropbox} this
33603          */
33604         "resize" : true,
33605         /**
33606          * @event rotate
33607          * Fire when rotate the image
33608          * @param {Roo.bootstrap.UploadCropbox} this
33609          * @param {String} pos
33610          */
33611         "rotate" : true,
33612         /**
33613          * @event inspect
33614          * Fire when inspect the file
33615          * @param {Roo.bootstrap.UploadCropbox} this
33616          * @param {Object} file
33617          */
33618         "inspect" : true,
33619         /**
33620          * @event upload
33621          * Fire when xhr upload the file
33622          * @param {Roo.bootstrap.UploadCropbox} this
33623          * @param {Object} data
33624          */
33625         "upload" : true,
33626         /**
33627          * @event arrange
33628          * Fire when arrange the file data
33629          * @param {Roo.bootstrap.UploadCropbox} this
33630          * @param {Object} formData
33631          */
33632         "arrange" : true
33633     });
33634     
33635     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
33636 };
33637
33638 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
33639     
33640     emptyText : 'Click to upload image',
33641     rotateNotify : 'Image is too small to rotate',
33642     errorTimeout : 3000,
33643     scale : 0,
33644     baseScale : 1,
33645     rotate : 0,
33646     dragable : false,
33647     pinching : false,
33648     mouseX : 0,
33649     mouseY : 0,
33650     cropData : false,
33651     minWidth : 300,
33652     minHeight : 300,
33653     file : false,
33654     exif : {},
33655     baseRotate : 1,
33656     cropType : 'image/jpeg',
33657     buttons : false,
33658     canvasLoaded : false,
33659     isDocument : false,
33660     method : 'POST',
33661     paramName : 'imageUpload',
33662     loadMask : true,
33663     loadingText : 'Loading...',
33664     maskEl : false,
33665     
33666     getAutoCreate : function()
33667     {
33668         var cfg = {
33669             tag : 'div',
33670             cls : 'roo-upload-cropbox',
33671             cn : [
33672                 {
33673                     tag : 'input',
33674                     cls : 'roo-upload-cropbox-selector',
33675                     type : 'file'
33676                 },
33677                 {
33678                     tag : 'div',
33679                     cls : 'roo-upload-cropbox-body',
33680                     style : 'cursor:pointer',
33681                     cn : [
33682                         {
33683                             tag : 'div',
33684                             cls : 'roo-upload-cropbox-preview'
33685                         },
33686                         {
33687                             tag : 'div',
33688                             cls : 'roo-upload-cropbox-thumb'
33689                         },
33690                         {
33691                             tag : 'div',
33692                             cls : 'roo-upload-cropbox-empty-notify',
33693                             html : this.emptyText
33694                         },
33695                         {
33696                             tag : 'div',
33697                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
33698                             html : this.rotateNotify
33699                         }
33700                     ]
33701                 },
33702                 {
33703                     tag : 'div',
33704                     cls : 'roo-upload-cropbox-footer',
33705                     cn : {
33706                         tag : 'div',
33707                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
33708                         cn : []
33709                     }
33710                 }
33711             ]
33712         };
33713         
33714         return cfg;
33715     },
33716     
33717     onRender : function(ct, position)
33718     {
33719         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
33720         
33721         if (this.buttons.length) {
33722             
33723             Roo.each(this.buttons, function(bb) {
33724                 
33725                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
33726                 
33727                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
33728                 
33729             }, this);
33730         }
33731         
33732         if(this.loadMask){
33733             this.maskEl = this.el;
33734         }
33735     },
33736     
33737     initEvents : function()
33738     {
33739         this.urlAPI = (window.createObjectURL && window) || 
33740                                 (window.URL && URL.revokeObjectURL && URL) || 
33741                                 (window.webkitURL && webkitURL);
33742                         
33743         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
33744         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33745         
33746         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
33747         this.selectorEl.hide();
33748         
33749         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
33750         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33751         
33752         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
33753         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33754         this.thumbEl.hide();
33755         
33756         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
33757         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33758         
33759         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
33760         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33761         this.errorEl.hide();
33762         
33763         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
33764         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33765         this.footerEl.hide();
33766         
33767         this.setThumbBoxSize();
33768         
33769         this.bind();
33770         
33771         this.resize();
33772         
33773         this.fireEvent('initial', this);
33774     },
33775
33776     bind : function()
33777     {
33778         var _this = this;
33779         
33780         window.addEventListener("resize", function() { _this.resize(); } );
33781         
33782         this.bodyEl.on('click', this.beforeSelectFile, this);
33783         
33784         if(Roo.isTouch){
33785             this.bodyEl.on('touchstart', this.onTouchStart, this);
33786             this.bodyEl.on('touchmove', this.onTouchMove, this);
33787             this.bodyEl.on('touchend', this.onTouchEnd, this);
33788         }
33789         
33790         if(!Roo.isTouch){
33791             this.bodyEl.on('mousedown', this.onMouseDown, this);
33792             this.bodyEl.on('mousemove', this.onMouseMove, this);
33793             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
33794             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
33795             Roo.get(document).on('mouseup', this.onMouseUp, this);
33796         }
33797         
33798         this.selectorEl.on('change', this.onFileSelected, this);
33799     },
33800     
33801     reset : function()
33802     {    
33803         this.scale = 0;
33804         this.baseScale = 1;
33805         this.rotate = 0;
33806         this.baseRotate = 1;
33807         this.dragable = false;
33808         this.pinching = false;
33809         this.mouseX = 0;
33810         this.mouseY = 0;
33811         this.cropData = false;
33812         this.notifyEl.dom.innerHTML = this.emptyText;
33813         
33814         this.selectorEl.dom.value = '';
33815         
33816     },
33817     
33818     resize : function()
33819     {
33820         if(this.fireEvent('resize', this) != false){
33821             this.setThumbBoxPosition();
33822             this.setCanvasPosition();
33823         }
33824     },
33825     
33826     onFooterButtonClick : function(e, el, o, type)
33827     {
33828         switch (type) {
33829             case 'rotate-left' :
33830                 this.onRotateLeft(e);
33831                 break;
33832             case 'rotate-right' :
33833                 this.onRotateRight(e);
33834                 break;
33835             case 'picture' :
33836                 this.beforeSelectFile(e);
33837                 break;
33838             case 'trash' :
33839                 this.trash(e);
33840                 break;
33841             case 'crop' :
33842                 this.crop(e);
33843                 break;
33844             case 'download' :
33845                 this.download(e);
33846                 break;
33847             default :
33848                 break;
33849         }
33850         
33851         this.fireEvent('footerbuttonclick', this, type);
33852     },
33853     
33854     beforeSelectFile : function(e)
33855     {
33856         e.preventDefault();
33857         
33858         if(this.fireEvent('beforeselectfile', this) != false){
33859             this.selectorEl.dom.click();
33860         }
33861     },
33862     
33863     onFileSelected : function(e)
33864     {
33865         e.preventDefault();
33866         
33867         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
33868             return;
33869         }
33870         
33871         var file = this.selectorEl.dom.files[0];
33872         
33873         if(this.fireEvent('inspect', this, file) != false){
33874             this.prepare(file);
33875         }
33876         
33877     },
33878     
33879     trash : function(e)
33880     {
33881         this.fireEvent('trash', this);
33882     },
33883     
33884     download : function(e)
33885     {
33886         this.fireEvent('download', this);
33887     },
33888     
33889     loadCanvas : function(src)
33890     {   
33891         if(this.fireEvent('beforeloadcanvas', this, src) != false){
33892             
33893             this.reset();
33894             
33895             this.imageEl = document.createElement('img');
33896             
33897             var _this = this;
33898             
33899             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
33900             
33901             this.imageEl.src = src;
33902         }
33903     },
33904     
33905     onLoadCanvas : function()
33906     {   
33907         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
33908         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
33909         
33910         this.bodyEl.un('click', this.beforeSelectFile, this);
33911         
33912         this.notifyEl.hide();
33913         this.thumbEl.show();
33914         this.footerEl.show();
33915         
33916         this.baseRotateLevel();
33917         
33918         if(this.isDocument){
33919             this.setThumbBoxSize();
33920         }
33921         
33922         this.setThumbBoxPosition();
33923         
33924         this.baseScaleLevel();
33925         
33926         this.draw();
33927         
33928         this.resize();
33929         
33930         this.canvasLoaded = true;
33931         
33932         if(this.loadMask){
33933             this.maskEl.unmask();
33934         }
33935         
33936     },
33937     
33938     setCanvasPosition : function()
33939     {   
33940         if(!this.canvasEl){
33941             return;
33942         }
33943         
33944         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
33945         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
33946         
33947         this.previewEl.setLeft(pw);
33948         this.previewEl.setTop(ph);
33949         
33950     },
33951     
33952     onMouseDown : function(e)
33953     {   
33954         e.stopEvent();
33955         
33956         this.dragable = true;
33957         this.pinching = false;
33958         
33959         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
33960             this.dragable = false;
33961             return;
33962         }
33963         
33964         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33965         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33966         
33967     },
33968     
33969     onMouseMove : function(e)
33970     {   
33971         e.stopEvent();
33972         
33973         if(!this.canvasLoaded){
33974             return;
33975         }
33976         
33977         if (!this.dragable){
33978             return;
33979         }
33980         
33981         var minX = Math.ceil(this.thumbEl.getLeft(true));
33982         var minY = Math.ceil(this.thumbEl.getTop(true));
33983         
33984         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
33985         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
33986         
33987         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33988         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33989         
33990         x = x - this.mouseX;
33991         y = y - this.mouseY;
33992         
33993         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
33994         var bgY = Math.ceil(y + this.previewEl.getTop(true));
33995         
33996         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
33997         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
33998         
33999         this.previewEl.setLeft(bgX);
34000         this.previewEl.setTop(bgY);
34001         
34002         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
34003         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
34004     },
34005     
34006     onMouseUp : function(e)
34007     {   
34008         e.stopEvent();
34009         
34010         this.dragable = false;
34011     },
34012     
34013     onMouseWheel : function(e)
34014     {   
34015         e.stopEvent();
34016         
34017         this.startScale = this.scale;
34018         
34019         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
34020         
34021         if(!this.zoomable()){
34022             this.scale = this.startScale;
34023             return;
34024         }
34025         
34026         this.draw();
34027         
34028         return;
34029     },
34030     
34031     zoomable : function()
34032     {
34033         var minScale = this.thumbEl.getWidth() / this.minWidth;
34034         
34035         if(this.minWidth < this.minHeight){
34036             minScale = this.thumbEl.getHeight() / this.minHeight;
34037         }
34038         
34039         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
34040         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
34041         
34042         if(
34043                 this.isDocument &&
34044                 (this.rotate == 0 || this.rotate == 180) && 
34045                 (
34046                     width > this.imageEl.OriginWidth || 
34047                     height > this.imageEl.OriginHeight ||
34048                     (width < this.minWidth && height < this.minHeight)
34049                 )
34050         ){
34051             return false;
34052         }
34053         
34054         if(
34055                 this.isDocument &&
34056                 (this.rotate == 90 || this.rotate == 270) && 
34057                 (
34058                     width > this.imageEl.OriginWidth || 
34059                     height > this.imageEl.OriginHeight ||
34060                     (width < this.minHeight && height < this.minWidth)
34061                 )
34062         ){
34063             return false;
34064         }
34065         
34066         if(
34067                 !this.isDocument &&
34068                 (this.rotate == 0 || this.rotate == 180) && 
34069                 (
34070                     width < this.minWidth || 
34071                     width > this.imageEl.OriginWidth || 
34072                     height < this.minHeight || 
34073                     height > this.imageEl.OriginHeight
34074                 )
34075         ){
34076             return false;
34077         }
34078         
34079         if(
34080                 !this.isDocument &&
34081                 (this.rotate == 90 || this.rotate == 270) && 
34082                 (
34083                     width < this.minHeight || 
34084                     width > this.imageEl.OriginWidth || 
34085                     height < this.minWidth || 
34086                     height > this.imageEl.OriginHeight
34087                 )
34088         ){
34089             return false;
34090         }
34091         
34092         return true;
34093         
34094     },
34095     
34096     onRotateLeft : function(e)
34097     {   
34098         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
34099             
34100             var minScale = this.thumbEl.getWidth() / this.minWidth;
34101             
34102             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
34103             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
34104             
34105             this.startScale = this.scale;
34106             
34107             while (this.getScaleLevel() < minScale){
34108             
34109                 this.scale = this.scale + 1;
34110                 
34111                 if(!this.zoomable()){
34112                     break;
34113                 }
34114                 
34115                 if(
34116                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
34117                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
34118                 ){
34119                     continue;
34120                 }
34121                 
34122                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
34123
34124                 this.draw();
34125                 
34126                 return;
34127             }
34128             
34129             this.scale = this.startScale;
34130             
34131             this.onRotateFail();
34132             
34133             return false;
34134         }
34135         
34136         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
34137
34138         if(this.isDocument){
34139             this.setThumbBoxSize();
34140             this.setThumbBoxPosition();
34141             this.setCanvasPosition();
34142         }
34143         
34144         this.draw();
34145         
34146         this.fireEvent('rotate', this, 'left');
34147         
34148     },
34149     
34150     onRotateRight : function(e)
34151     {
34152         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
34153             
34154             var minScale = this.thumbEl.getWidth() / this.minWidth;
34155         
34156             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
34157             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
34158             
34159             this.startScale = this.scale;
34160             
34161             while (this.getScaleLevel() < minScale){
34162             
34163                 this.scale = this.scale + 1;
34164                 
34165                 if(!this.zoomable()){
34166                     break;
34167                 }
34168                 
34169                 if(
34170                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
34171                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
34172                 ){
34173                     continue;
34174                 }
34175                 
34176                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
34177
34178                 this.draw();
34179                 
34180                 return;
34181             }
34182             
34183             this.scale = this.startScale;
34184             
34185             this.onRotateFail();
34186             
34187             return false;
34188         }
34189         
34190         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
34191
34192         if(this.isDocument){
34193             this.setThumbBoxSize();
34194             this.setThumbBoxPosition();
34195             this.setCanvasPosition();
34196         }
34197         
34198         this.draw();
34199         
34200         this.fireEvent('rotate', this, 'right');
34201     },
34202     
34203     onRotateFail : function()
34204     {
34205         this.errorEl.show(true);
34206         
34207         var _this = this;
34208         
34209         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
34210     },
34211     
34212     draw : function()
34213     {
34214         this.previewEl.dom.innerHTML = '';
34215         
34216         var canvasEl = document.createElement("canvas");
34217         
34218         var contextEl = canvasEl.getContext("2d");
34219         
34220         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34221         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34222         var center = this.imageEl.OriginWidth / 2;
34223         
34224         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
34225             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34226             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34227             center = this.imageEl.OriginHeight / 2;
34228         }
34229         
34230         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
34231         
34232         contextEl.translate(center, center);
34233         contextEl.rotate(this.rotate * Math.PI / 180);
34234
34235         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34236         
34237         this.canvasEl = document.createElement("canvas");
34238         
34239         this.contextEl = this.canvasEl.getContext("2d");
34240         
34241         switch (this.rotate) {
34242             case 0 :
34243                 
34244                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34245                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34246                 
34247                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34248                 
34249                 break;
34250             case 90 : 
34251                 
34252                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34253                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34254                 
34255                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34256                     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);
34257                     break;
34258                 }
34259                 
34260                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34261                 
34262                 break;
34263             case 180 :
34264                 
34265                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34266                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34267                 
34268                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34269                     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);
34270                     break;
34271                 }
34272                 
34273                 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);
34274                 
34275                 break;
34276             case 270 :
34277                 
34278                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34279                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34280         
34281                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34282                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34283                     break;
34284                 }
34285                 
34286                 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);
34287                 
34288                 break;
34289             default : 
34290                 break;
34291         }
34292         
34293         this.previewEl.appendChild(this.canvasEl);
34294         
34295         this.setCanvasPosition();
34296     },
34297     
34298     crop : function()
34299     {
34300         if(!this.canvasLoaded){
34301             return;
34302         }
34303         
34304         var imageCanvas = document.createElement("canvas");
34305         
34306         var imageContext = imageCanvas.getContext("2d");
34307         
34308         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34309         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34310         
34311         var center = imageCanvas.width / 2;
34312         
34313         imageContext.translate(center, center);
34314         
34315         imageContext.rotate(this.rotate * Math.PI / 180);
34316         
34317         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34318         
34319         var canvas = document.createElement("canvas");
34320         
34321         var context = canvas.getContext("2d");
34322                 
34323         canvas.width = this.minWidth;
34324         canvas.height = this.minHeight;
34325
34326         switch (this.rotate) {
34327             case 0 :
34328                 
34329                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34330                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34331                 
34332                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34333                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34334                 
34335                 var targetWidth = this.minWidth - 2 * x;
34336                 var targetHeight = this.minHeight - 2 * y;
34337                 
34338                 var scale = 1;
34339                 
34340                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34341                     scale = targetWidth / width;
34342                 }
34343                 
34344                 if(x > 0 && y == 0){
34345                     scale = targetHeight / height;
34346                 }
34347                 
34348                 if(x > 0 && y > 0){
34349                     scale = targetWidth / width;
34350                     
34351                     if(width < height){
34352                         scale = targetHeight / height;
34353                     }
34354                 }
34355                 
34356                 context.scale(scale, scale);
34357                 
34358                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34359                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34360
34361                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34362                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34363
34364                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34365                 
34366                 break;
34367             case 90 : 
34368                 
34369                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34370                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34371                 
34372                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34373                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34374                 
34375                 var targetWidth = this.minWidth - 2 * x;
34376                 var targetHeight = this.minHeight - 2 * y;
34377                 
34378                 var scale = 1;
34379                 
34380                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34381                     scale = targetWidth / width;
34382                 }
34383                 
34384                 if(x > 0 && y == 0){
34385                     scale = targetHeight / height;
34386                 }
34387                 
34388                 if(x > 0 && y > 0){
34389                     scale = targetWidth / width;
34390                     
34391                     if(width < height){
34392                         scale = targetHeight / height;
34393                     }
34394                 }
34395                 
34396                 context.scale(scale, scale);
34397                 
34398                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34399                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34400
34401                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34402                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34403                 
34404                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34405                 
34406                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34407                 
34408                 break;
34409             case 180 :
34410                 
34411                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34412                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34413                 
34414                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34415                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34416                 
34417                 var targetWidth = this.minWidth - 2 * x;
34418                 var targetHeight = this.minHeight - 2 * y;
34419                 
34420                 var scale = 1;
34421                 
34422                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34423                     scale = targetWidth / width;
34424                 }
34425                 
34426                 if(x > 0 && y == 0){
34427                     scale = targetHeight / height;
34428                 }
34429                 
34430                 if(x > 0 && y > 0){
34431                     scale = targetWidth / width;
34432                     
34433                     if(width < height){
34434                         scale = targetHeight / height;
34435                     }
34436                 }
34437                 
34438                 context.scale(scale, scale);
34439                 
34440                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34441                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34442
34443                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34444                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34445
34446                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34447                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34448                 
34449                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34450                 
34451                 break;
34452             case 270 :
34453                 
34454                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34455                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34456                 
34457                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34458                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34459                 
34460                 var targetWidth = this.minWidth - 2 * x;
34461                 var targetHeight = this.minHeight - 2 * y;
34462                 
34463                 var scale = 1;
34464                 
34465                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34466                     scale = targetWidth / width;
34467                 }
34468                 
34469                 if(x > 0 && y == 0){
34470                     scale = targetHeight / height;
34471                 }
34472                 
34473                 if(x > 0 && y > 0){
34474                     scale = targetWidth / width;
34475                     
34476                     if(width < height){
34477                         scale = targetHeight / height;
34478                     }
34479                 }
34480                 
34481                 context.scale(scale, scale);
34482                 
34483                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34484                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34485
34486                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34487                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34488                 
34489                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34490                 
34491                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34492                 
34493                 break;
34494             default : 
34495                 break;
34496         }
34497         
34498         this.cropData = canvas.toDataURL(this.cropType);
34499         
34500         if(this.fireEvent('crop', this, this.cropData) !== false){
34501             this.process(this.file, this.cropData);
34502         }
34503         
34504         return;
34505         
34506     },
34507     
34508     setThumbBoxSize : function()
34509     {
34510         var width, height;
34511         
34512         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
34513             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
34514             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
34515             
34516             this.minWidth = width;
34517             this.minHeight = height;
34518             
34519             if(this.rotate == 90 || this.rotate == 270){
34520                 this.minWidth = height;
34521                 this.minHeight = width;
34522             }
34523         }
34524         
34525         height = 300;
34526         width = Math.ceil(this.minWidth * height / this.minHeight);
34527         
34528         if(this.minWidth > this.minHeight){
34529             width = 300;
34530             height = Math.ceil(this.minHeight * width / this.minWidth);
34531         }
34532         
34533         this.thumbEl.setStyle({
34534             width : width + 'px',
34535             height : height + 'px'
34536         });
34537
34538         return;
34539             
34540     },
34541     
34542     setThumbBoxPosition : function()
34543     {
34544         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
34545         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
34546         
34547         this.thumbEl.setLeft(x);
34548         this.thumbEl.setTop(y);
34549         
34550     },
34551     
34552     baseRotateLevel : function()
34553     {
34554         this.baseRotate = 1;
34555         
34556         if(
34557                 typeof(this.exif) != 'undefined' &&
34558                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
34559                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
34560         ){
34561             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
34562         }
34563         
34564         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
34565         
34566     },
34567     
34568     baseScaleLevel : function()
34569     {
34570         var width, height;
34571         
34572         if(this.isDocument){
34573             
34574             if(this.baseRotate == 6 || this.baseRotate == 8){
34575             
34576                 height = this.thumbEl.getHeight();
34577                 this.baseScale = height / this.imageEl.OriginWidth;
34578
34579                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
34580                     width = this.thumbEl.getWidth();
34581                     this.baseScale = width / this.imageEl.OriginHeight;
34582                 }
34583
34584                 return;
34585             }
34586
34587             height = this.thumbEl.getHeight();
34588             this.baseScale = height / this.imageEl.OriginHeight;
34589
34590             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
34591                 width = this.thumbEl.getWidth();
34592                 this.baseScale = width / this.imageEl.OriginWidth;
34593             }
34594
34595             return;
34596         }
34597         
34598         if(this.baseRotate == 6 || this.baseRotate == 8){
34599             
34600             width = this.thumbEl.getHeight();
34601             this.baseScale = width / this.imageEl.OriginHeight;
34602             
34603             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
34604                 height = this.thumbEl.getWidth();
34605                 this.baseScale = height / this.imageEl.OriginHeight;
34606             }
34607             
34608             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34609                 height = this.thumbEl.getWidth();
34610                 this.baseScale = height / this.imageEl.OriginHeight;
34611                 
34612                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
34613                     width = this.thumbEl.getHeight();
34614                     this.baseScale = width / this.imageEl.OriginWidth;
34615                 }
34616             }
34617             
34618             return;
34619         }
34620         
34621         width = this.thumbEl.getWidth();
34622         this.baseScale = width / this.imageEl.OriginWidth;
34623         
34624         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
34625             height = this.thumbEl.getHeight();
34626             this.baseScale = height / this.imageEl.OriginHeight;
34627         }
34628         
34629         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34630             
34631             height = this.thumbEl.getHeight();
34632             this.baseScale = height / this.imageEl.OriginHeight;
34633             
34634             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
34635                 width = this.thumbEl.getWidth();
34636                 this.baseScale = width / this.imageEl.OriginWidth;
34637             }
34638             
34639         }
34640         
34641         return;
34642     },
34643     
34644     getScaleLevel : function()
34645     {
34646         return this.baseScale * Math.pow(1.1, this.scale);
34647     },
34648     
34649     onTouchStart : function(e)
34650     {
34651         if(!this.canvasLoaded){
34652             this.beforeSelectFile(e);
34653             return;
34654         }
34655         
34656         var touches = e.browserEvent.touches;
34657         
34658         if(!touches){
34659             return;
34660         }
34661         
34662         if(touches.length == 1){
34663             this.onMouseDown(e);
34664             return;
34665         }
34666         
34667         if(touches.length != 2){
34668             return;
34669         }
34670         
34671         var coords = [];
34672         
34673         for(var i = 0, finger; finger = touches[i]; i++){
34674             coords.push(finger.pageX, finger.pageY);
34675         }
34676         
34677         var x = Math.pow(coords[0] - coords[2], 2);
34678         var y = Math.pow(coords[1] - coords[3], 2);
34679         
34680         this.startDistance = Math.sqrt(x + y);
34681         
34682         this.startScale = this.scale;
34683         
34684         this.pinching = true;
34685         this.dragable = false;
34686         
34687     },
34688     
34689     onTouchMove : function(e)
34690     {
34691         if(!this.pinching && !this.dragable){
34692             return;
34693         }
34694         
34695         var touches = e.browserEvent.touches;
34696         
34697         if(!touches){
34698             return;
34699         }
34700         
34701         if(this.dragable){
34702             this.onMouseMove(e);
34703             return;
34704         }
34705         
34706         var coords = [];
34707         
34708         for(var i = 0, finger; finger = touches[i]; i++){
34709             coords.push(finger.pageX, finger.pageY);
34710         }
34711         
34712         var x = Math.pow(coords[0] - coords[2], 2);
34713         var y = Math.pow(coords[1] - coords[3], 2);
34714         
34715         this.endDistance = Math.sqrt(x + y);
34716         
34717         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
34718         
34719         if(!this.zoomable()){
34720             this.scale = this.startScale;
34721             return;
34722         }
34723         
34724         this.draw();
34725         
34726     },
34727     
34728     onTouchEnd : function(e)
34729     {
34730         this.pinching = false;
34731         this.dragable = false;
34732         
34733     },
34734     
34735     process : function(file, crop)
34736     {
34737         if(this.loadMask){
34738             this.maskEl.mask(this.loadingText);
34739         }
34740         
34741         this.xhr = new XMLHttpRequest();
34742         
34743         file.xhr = this.xhr;
34744
34745         this.xhr.open(this.method, this.url, true);
34746         
34747         var headers = {
34748             "Accept": "application/json",
34749             "Cache-Control": "no-cache",
34750             "X-Requested-With": "XMLHttpRequest"
34751         };
34752         
34753         for (var headerName in headers) {
34754             var headerValue = headers[headerName];
34755             if (headerValue) {
34756                 this.xhr.setRequestHeader(headerName, headerValue);
34757             }
34758         }
34759         
34760         var _this = this;
34761         
34762         this.xhr.onload = function()
34763         {
34764             _this.xhrOnLoad(_this.xhr);
34765         }
34766         
34767         this.xhr.onerror = function()
34768         {
34769             _this.xhrOnError(_this.xhr);
34770         }
34771         
34772         var formData = new FormData();
34773
34774         formData.append('returnHTML', 'NO');
34775         
34776         if(crop){
34777             formData.append('crop', crop);
34778         }
34779         
34780         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
34781             formData.append(this.paramName, file, file.name);
34782         }
34783         
34784         if(typeof(file.filename) != 'undefined'){
34785             formData.append('filename', file.filename);
34786         }
34787         
34788         if(typeof(file.mimetype) != 'undefined'){
34789             formData.append('mimetype', file.mimetype);
34790         }
34791         
34792         if(this.fireEvent('arrange', this, formData) != false){
34793             this.xhr.send(formData);
34794         };
34795     },
34796     
34797     xhrOnLoad : function(xhr)
34798     {
34799         if(this.loadMask){
34800             this.maskEl.unmask();
34801         }
34802         
34803         if (xhr.readyState !== 4) {
34804             this.fireEvent('exception', this, xhr);
34805             return;
34806         }
34807
34808         var response = Roo.decode(xhr.responseText);
34809         
34810         if(!response.success){
34811             this.fireEvent('exception', this, xhr);
34812             return;
34813         }
34814         
34815         var response = Roo.decode(xhr.responseText);
34816         
34817         this.fireEvent('upload', this, response);
34818         
34819     },
34820     
34821     xhrOnError : function()
34822     {
34823         if(this.loadMask){
34824             this.maskEl.unmask();
34825         }
34826         
34827         Roo.log('xhr on error');
34828         
34829         var response = Roo.decode(xhr.responseText);
34830           
34831         Roo.log(response);
34832         
34833     },
34834     
34835     prepare : function(file)
34836     {   
34837         if(this.loadMask){
34838             this.maskEl.mask(this.loadingText);
34839         }
34840         
34841         this.file = false;
34842         this.exif = {};
34843         
34844         if(typeof(file) === 'string'){
34845             this.loadCanvas(file);
34846             return;
34847         }
34848         
34849         if(!file || !this.urlAPI){
34850             return;
34851         }
34852         
34853         this.file = file;
34854         this.cropType = file.type;
34855         
34856         var _this = this;
34857         
34858         if(this.fireEvent('prepare', this, this.file) != false){
34859             
34860             var reader = new FileReader();
34861             
34862             reader.onload = function (e) {
34863                 if (e.target.error) {
34864                     Roo.log(e.target.error);
34865                     return;
34866                 }
34867                 
34868                 var buffer = e.target.result,
34869                     dataView = new DataView(buffer),
34870                     offset = 2,
34871                     maxOffset = dataView.byteLength - 4,
34872                     markerBytes,
34873                     markerLength;
34874                 
34875                 if (dataView.getUint16(0) === 0xffd8) {
34876                     while (offset < maxOffset) {
34877                         markerBytes = dataView.getUint16(offset);
34878                         
34879                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
34880                             markerLength = dataView.getUint16(offset + 2) + 2;
34881                             if (offset + markerLength > dataView.byteLength) {
34882                                 Roo.log('Invalid meta data: Invalid segment size.');
34883                                 break;
34884                             }
34885                             
34886                             if(markerBytes == 0xffe1){
34887                                 _this.parseExifData(
34888                                     dataView,
34889                                     offset,
34890                                     markerLength
34891                                 );
34892                             }
34893                             
34894                             offset += markerLength;
34895                             
34896                             continue;
34897                         }
34898                         
34899                         break;
34900                     }
34901                     
34902                 }
34903                 
34904                 var url = _this.urlAPI.createObjectURL(_this.file);
34905                 
34906                 _this.loadCanvas(url);
34907                 
34908                 return;
34909             }
34910             
34911             reader.readAsArrayBuffer(this.file);
34912             
34913         }
34914         
34915     },
34916     
34917     parseExifData : function(dataView, offset, length)
34918     {
34919         var tiffOffset = offset + 10,
34920             littleEndian,
34921             dirOffset;
34922     
34923         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34924             // No Exif data, might be XMP data instead
34925             return;
34926         }
34927         
34928         // Check for the ASCII code for "Exif" (0x45786966):
34929         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34930             // No Exif data, might be XMP data instead
34931             return;
34932         }
34933         if (tiffOffset + 8 > dataView.byteLength) {
34934             Roo.log('Invalid Exif data: Invalid segment size.');
34935             return;
34936         }
34937         // Check for the two null bytes:
34938         if (dataView.getUint16(offset + 8) !== 0x0000) {
34939             Roo.log('Invalid Exif data: Missing byte alignment offset.');
34940             return;
34941         }
34942         // Check the byte alignment:
34943         switch (dataView.getUint16(tiffOffset)) {
34944         case 0x4949:
34945             littleEndian = true;
34946             break;
34947         case 0x4D4D:
34948             littleEndian = false;
34949             break;
34950         default:
34951             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
34952             return;
34953         }
34954         // Check for the TIFF tag marker (0x002A):
34955         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
34956             Roo.log('Invalid Exif data: Missing TIFF marker.');
34957             return;
34958         }
34959         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
34960         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
34961         
34962         this.parseExifTags(
34963             dataView,
34964             tiffOffset,
34965             tiffOffset + dirOffset,
34966             littleEndian
34967         );
34968     },
34969     
34970     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
34971     {
34972         var tagsNumber,
34973             dirEndOffset,
34974             i;
34975         if (dirOffset + 6 > dataView.byteLength) {
34976             Roo.log('Invalid Exif data: Invalid directory offset.');
34977             return;
34978         }
34979         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
34980         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
34981         if (dirEndOffset + 4 > dataView.byteLength) {
34982             Roo.log('Invalid Exif data: Invalid directory size.');
34983             return;
34984         }
34985         for (i = 0; i < tagsNumber; i += 1) {
34986             this.parseExifTag(
34987                 dataView,
34988                 tiffOffset,
34989                 dirOffset + 2 + 12 * i, // tag offset
34990                 littleEndian
34991             );
34992         }
34993         // Return the offset to the next directory:
34994         return dataView.getUint32(dirEndOffset, littleEndian);
34995     },
34996     
34997     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
34998     {
34999         var tag = dataView.getUint16(offset, littleEndian);
35000         
35001         this.exif[tag] = this.getExifValue(
35002             dataView,
35003             tiffOffset,
35004             offset,
35005             dataView.getUint16(offset + 2, littleEndian), // tag type
35006             dataView.getUint32(offset + 4, littleEndian), // tag length
35007             littleEndian
35008         );
35009     },
35010     
35011     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
35012     {
35013         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
35014             tagSize,
35015             dataOffset,
35016             values,
35017             i,
35018             str,
35019             c;
35020     
35021         if (!tagType) {
35022             Roo.log('Invalid Exif data: Invalid tag type.');
35023             return;
35024         }
35025         
35026         tagSize = tagType.size * length;
35027         // Determine if the value is contained in the dataOffset bytes,
35028         // or if the value at the dataOffset is a pointer to the actual data:
35029         dataOffset = tagSize > 4 ?
35030                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
35031         if (dataOffset + tagSize > dataView.byteLength) {
35032             Roo.log('Invalid Exif data: Invalid data offset.');
35033             return;
35034         }
35035         if (length === 1) {
35036             return tagType.getValue(dataView, dataOffset, littleEndian);
35037         }
35038         values = [];
35039         for (i = 0; i < length; i += 1) {
35040             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
35041         }
35042         
35043         if (tagType.ascii) {
35044             str = '';
35045             // Concatenate the chars:
35046             for (i = 0; i < values.length; i += 1) {
35047                 c = values[i];
35048                 // Ignore the terminating NULL byte(s):
35049                 if (c === '\u0000') {
35050                     break;
35051                 }
35052                 str += c;
35053             }
35054             return str;
35055         }
35056         return values;
35057     }
35058     
35059 });
35060
35061 Roo.apply(Roo.bootstrap.UploadCropbox, {
35062     tags : {
35063         'Orientation': 0x0112
35064     },
35065     
35066     Orientation: {
35067             1: 0, //'top-left',
35068 //            2: 'top-right',
35069             3: 180, //'bottom-right',
35070 //            4: 'bottom-left',
35071 //            5: 'left-top',
35072             6: 90, //'right-top',
35073 //            7: 'right-bottom',
35074             8: 270 //'left-bottom'
35075     },
35076     
35077     exifTagTypes : {
35078         // byte, 8-bit unsigned int:
35079         1: {
35080             getValue: function (dataView, dataOffset) {
35081                 return dataView.getUint8(dataOffset);
35082             },
35083             size: 1
35084         },
35085         // ascii, 8-bit byte:
35086         2: {
35087             getValue: function (dataView, dataOffset) {
35088                 return String.fromCharCode(dataView.getUint8(dataOffset));
35089             },
35090             size: 1,
35091             ascii: true
35092         },
35093         // short, 16 bit int:
35094         3: {
35095             getValue: function (dataView, dataOffset, littleEndian) {
35096                 return dataView.getUint16(dataOffset, littleEndian);
35097             },
35098             size: 2
35099         },
35100         // long, 32 bit int:
35101         4: {
35102             getValue: function (dataView, dataOffset, littleEndian) {
35103                 return dataView.getUint32(dataOffset, littleEndian);
35104             },
35105             size: 4
35106         },
35107         // rational = two long values, first is numerator, second is denominator:
35108         5: {
35109             getValue: function (dataView, dataOffset, littleEndian) {
35110                 return dataView.getUint32(dataOffset, littleEndian) /
35111                     dataView.getUint32(dataOffset + 4, littleEndian);
35112             },
35113             size: 8
35114         },
35115         // slong, 32 bit signed int:
35116         9: {
35117             getValue: function (dataView, dataOffset, littleEndian) {
35118                 return dataView.getInt32(dataOffset, littleEndian);
35119             },
35120             size: 4
35121         },
35122         // srational, two slongs, first is numerator, second is denominator:
35123         10: {
35124             getValue: function (dataView, dataOffset, littleEndian) {
35125                 return dataView.getInt32(dataOffset, littleEndian) /
35126                     dataView.getInt32(dataOffset + 4, littleEndian);
35127             },
35128             size: 8
35129         }
35130     },
35131     
35132     footer : {
35133         STANDARD : [
35134             {
35135                 tag : 'div',
35136                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35137                 action : 'rotate-left',
35138                 cn : [
35139                     {
35140                         tag : 'button',
35141                         cls : 'btn btn-default',
35142                         html : '<i class="fa fa-undo"></i>'
35143                     }
35144                 ]
35145             },
35146             {
35147                 tag : 'div',
35148                 cls : 'btn-group roo-upload-cropbox-picture',
35149                 action : 'picture',
35150                 cn : [
35151                     {
35152                         tag : 'button',
35153                         cls : 'btn btn-default',
35154                         html : '<i class="fa fa-picture-o"></i>'
35155                     }
35156                 ]
35157             },
35158             {
35159                 tag : 'div',
35160                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35161                 action : 'rotate-right',
35162                 cn : [
35163                     {
35164                         tag : 'button',
35165                         cls : 'btn btn-default',
35166                         html : '<i class="fa fa-repeat"></i>'
35167                     }
35168                 ]
35169             }
35170         ],
35171         DOCUMENT : [
35172             {
35173                 tag : 'div',
35174                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35175                 action : 'rotate-left',
35176                 cn : [
35177                     {
35178                         tag : 'button',
35179                         cls : 'btn btn-default',
35180                         html : '<i class="fa fa-undo"></i>'
35181                     }
35182                 ]
35183             },
35184             {
35185                 tag : 'div',
35186                 cls : 'btn-group roo-upload-cropbox-download',
35187                 action : 'download',
35188                 cn : [
35189                     {
35190                         tag : 'button',
35191                         cls : 'btn btn-default',
35192                         html : '<i class="fa fa-download"></i>'
35193                     }
35194                 ]
35195             },
35196             {
35197                 tag : 'div',
35198                 cls : 'btn-group roo-upload-cropbox-crop',
35199                 action : 'crop',
35200                 cn : [
35201                     {
35202                         tag : 'button',
35203                         cls : 'btn btn-default',
35204                         html : '<i class="fa fa-crop"></i>'
35205                     }
35206                 ]
35207             },
35208             {
35209                 tag : 'div',
35210                 cls : 'btn-group roo-upload-cropbox-trash',
35211                 action : 'trash',
35212                 cn : [
35213                     {
35214                         tag : 'button',
35215                         cls : 'btn btn-default',
35216                         html : '<i class="fa fa-trash"></i>'
35217                     }
35218                 ]
35219             },
35220             {
35221                 tag : 'div',
35222                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35223                 action : 'rotate-right',
35224                 cn : [
35225                     {
35226                         tag : 'button',
35227                         cls : 'btn btn-default',
35228                         html : '<i class="fa fa-repeat"></i>'
35229                     }
35230                 ]
35231             }
35232         ],
35233         ROTATOR : [
35234             {
35235                 tag : 'div',
35236                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35237                 action : 'rotate-left',
35238                 cn : [
35239                     {
35240                         tag : 'button',
35241                         cls : 'btn btn-default',
35242                         html : '<i class="fa fa-undo"></i>'
35243                     }
35244                 ]
35245             },
35246             {
35247                 tag : 'div',
35248                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35249                 action : 'rotate-right',
35250                 cn : [
35251                     {
35252                         tag : 'button',
35253                         cls : 'btn btn-default',
35254                         html : '<i class="fa fa-repeat"></i>'
35255                     }
35256                 ]
35257             }
35258         ]
35259     }
35260 });
35261
35262 /*
35263 * Licence: LGPL
35264 */
35265
35266 /**
35267  * @class Roo.bootstrap.DocumentManager
35268  * @extends Roo.bootstrap.Component
35269  * Bootstrap DocumentManager class
35270  * @cfg {String} paramName default 'imageUpload'
35271  * @cfg {String} toolTipName default 'filename'
35272  * @cfg {String} method default POST
35273  * @cfg {String} url action url
35274  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
35275  * @cfg {Boolean} multiple multiple upload default true
35276  * @cfg {Number} thumbSize default 300
35277  * @cfg {String} fieldLabel
35278  * @cfg {Number} labelWidth default 4
35279  * @cfg {String} labelAlign (left|top) default left
35280  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
35281 * @cfg {Number} labellg set the width of label (1-12)
35282  * @cfg {Number} labelmd set the width of label (1-12)
35283  * @cfg {Number} labelsm set the width of label (1-12)
35284  * @cfg {Number} labelxs set the width of label (1-12)
35285  * 
35286  * @constructor
35287  * Create a new DocumentManager
35288  * @param {Object} config The config object
35289  */
35290
35291 Roo.bootstrap.DocumentManager = function(config){
35292     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
35293     
35294     this.files = [];
35295     this.delegates = [];
35296     
35297     this.addEvents({
35298         /**
35299          * @event initial
35300          * Fire when initial the DocumentManager
35301          * @param {Roo.bootstrap.DocumentManager} this
35302          */
35303         "initial" : true,
35304         /**
35305          * @event inspect
35306          * inspect selected file
35307          * @param {Roo.bootstrap.DocumentManager} this
35308          * @param {File} file
35309          */
35310         "inspect" : true,
35311         /**
35312          * @event exception
35313          * Fire when xhr load exception
35314          * @param {Roo.bootstrap.DocumentManager} this
35315          * @param {XMLHttpRequest} xhr
35316          */
35317         "exception" : true,
35318         /**
35319          * @event afterupload
35320          * Fire when xhr load exception
35321          * @param {Roo.bootstrap.DocumentManager} this
35322          * @param {XMLHttpRequest} xhr
35323          */
35324         "afterupload" : true,
35325         /**
35326          * @event prepare
35327          * prepare the form data
35328          * @param {Roo.bootstrap.DocumentManager} this
35329          * @param {Object} formData
35330          */
35331         "prepare" : true,
35332         /**
35333          * @event remove
35334          * Fire when remove the file
35335          * @param {Roo.bootstrap.DocumentManager} this
35336          * @param {Object} file
35337          */
35338         "remove" : true,
35339         /**
35340          * @event refresh
35341          * Fire after refresh the file
35342          * @param {Roo.bootstrap.DocumentManager} this
35343          */
35344         "refresh" : true,
35345         /**
35346          * @event click
35347          * Fire after click the image
35348          * @param {Roo.bootstrap.DocumentManager} this
35349          * @param {Object} file
35350          */
35351         "click" : true,
35352         /**
35353          * @event edit
35354          * Fire when upload a image and editable set to true
35355          * @param {Roo.bootstrap.DocumentManager} this
35356          * @param {Object} file
35357          */
35358         "edit" : true,
35359         /**
35360          * @event beforeselectfile
35361          * Fire before select file
35362          * @param {Roo.bootstrap.DocumentManager} this
35363          */
35364         "beforeselectfile" : true,
35365         /**
35366          * @event process
35367          * Fire before process file
35368          * @param {Roo.bootstrap.DocumentManager} this
35369          * @param {Object} file
35370          */
35371         "process" : true,
35372         /**
35373          * @event previewrendered
35374          * Fire when preview rendered
35375          * @param {Roo.bootstrap.DocumentManager} this
35376          * @param {Object} file
35377          */
35378         "previewrendered" : true,
35379         /**
35380          */
35381         "previewResize" : true
35382         
35383     });
35384 };
35385
35386 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
35387     
35388     boxes : 0,
35389     inputName : '',
35390     thumbSize : 300,
35391     multiple : true,
35392     files : false,
35393     method : 'POST',
35394     url : '',
35395     paramName : 'imageUpload',
35396     toolTipName : 'filename',
35397     fieldLabel : '',
35398     labelWidth : 4,
35399     labelAlign : 'left',
35400     editable : true,
35401     delegates : false,
35402     xhr : false, 
35403     
35404     labellg : 0,
35405     labelmd : 0,
35406     labelsm : 0,
35407     labelxs : 0,
35408     
35409     getAutoCreate : function()
35410     {   
35411         var managerWidget = {
35412             tag : 'div',
35413             cls : 'roo-document-manager',
35414             cn : [
35415                 {
35416                     tag : 'input',
35417                     cls : 'roo-document-manager-selector',
35418                     type : 'file'
35419                 },
35420                 {
35421                     tag : 'div',
35422                     cls : 'roo-document-manager-uploader',
35423                     cn : [
35424                         {
35425                             tag : 'div',
35426                             cls : 'roo-document-manager-upload-btn',
35427                             html : '<i class="fa fa-plus"></i>'
35428                         }
35429                     ]
35430                     
35431                 }
35432             ]
35433         };
35434         
35435         var content = [
35436             {
35437                 tag : 'div',
35438                 cls : 'column col-md-12',
35439                 cn : managerWidget
35440             }
35441         ];
35442         
35443         if(this.fieldLabel.length){
35444             
35445             content = [
35446                 {
35447                     tag : 'div',
35448                     cls : 'column col-md-12',
35449                     html : this.fieldLabel
35450                 },
35451                 {
35452                     tag : 'div',
35453                     cls : 'column col-md-12',
35454                     cn : managerWidget
35455                 }
35456             ];
35457
35458             if(this.labelAlign == 'left'){
35459                 content = [
35460                     {
35461                         tag : 'div',
35462                         cls : 'column',
35463                         html : this.fieldLabel
35464                     },
35465                     {
35466                         tag : 'div',
35467                         cls : 'column',
35468                         cn : managerWidget
35469                     }
35470                 ];
35471                 
35472                 if(this.labelWidth > 12){
35473                     content[0].style = "width: " + this.labelWidth + 'px';
35474                 }
35475
35476                 if(this.labelWidth < 13 && this.labelmd == 0){
35477                     this.labelmd = this.labelWidth;
35478                 }
35479
35480                 if(this.labellg > 0){
35481                     content[0].cls += ' col-lg-' + this.labellg;
35482                     content[1].cls += ' col-lg-' + (12 - this.labellg);
35483                 }
35484
35485                 if(this.labelmd > 0){
35486                     content[0].cls += ' col-md-' + this.labelmd;
35487                     content[1].cls += ' col-md-' + (12 - this.labelmd);
35488                 }
35489
35490                 if(this.labelsm > 0){
35491                     content[0].cls += ' col-sm-' + this.labelsm;
35492                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
35493                 }
35494
35495                 if(this.labelxs > 0){
35496                     content[0].cls += ' col-xs-' + this.labelxs;
35497                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
35498                 }
35499                 
35500             }
35501         }
35502         
35503         var cfg = {
35504             tag : 'div',
35505             cls : 'row clearfix',
35506             cn : content
35507         };
35508         
35509         return cfg;
35510         
35511     },
35512     
35513     initEvents : function()
35514     {
35515         this.managerEl = this.el.select('.roo-document-manager', true).first();
35516         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35517         
35518         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
35519         this.selectorEl.hide();
35520         
35521         if(this.multiple){
35522             this.selectorEl.attr('multiple', 'multiple');
35523         }
35524         
35525         this.selectorEl.on('change', this.onFileSelected, this);
35526         
35527         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
35528         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35529         
35530         this.uploader.on('click', this.onUploaderClick, this);
35531         
35532         this.renderProgressDialog();
35533         
35534         var _this = this;
35535         
35536         window.addEventListener("resize", function() { _this.refresh(); } );
35537         
35538         this.fireEvent('initial', this);
35539     },
35540     
35541     renderProgressDialog : function()
35542     {
35543         var _this = this;
35544         
35545         this.progressDialog = new Roo.bootstrap.Modal({
35546             cls : 'roo-document-manager-progress-dialog',
35547             allow_close : false,
35548             animate : false,
35549             title : '',
35550             buttons : [
35551                 {
35552                     name  :'cancel',
35553                     weight : 'danger',
35554                     html : 'Cancel'
35555                 }
35556             ], 
35557             listeners : { 
35558                 btnclick : function() {
35559                     _this.uploadCancel();
35560                     this.hide();
35561                 }
35562             }
35563         });
35564          
35565         this.progressDialog.render(Roo.get(document.body));
35566          
35567         this.progress = new Roo.bootstrap.Progress({
35568             cls : 'roo-document-manager-progress',
35569             active : true,
35570             striped : true
35571         });
35572         
35573         this.progress.render(this.progressDialog.getChildContainer());
35574         
35575         this.progressBar = new Roo.bootstrap.ProgressBar({
35576             cls : 'roo-document-manager-progress-bar',
35577             aria_valuenow : 0,
35578             aria_valuemin : 0,
35579             aria_valuemax : 12,
35580             panel : 'success'
35581         });
35582         
35583         this.progressBar.render(this.progress.getChildContainer());
35584     },
35585     
35586     onUploaderClick : function(e)
35587     {
35588         e.preventDefault();
35589      
35590         if(this.fireEvent('beforeselectfile', this) != false){
35591             this.selectorEl.dom.click();
35592         }
35593         
35594     },
35595     
35596     onFileSelected : function(e)
35597     {
35598         e.preventDefault();
35599         
35600         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35601             return;
35602         }
35603         
35604         Roo.each(this.selectorEl.dom.files, function(file){
35605             if(this.fireEvent('inspect', this, file) != false){
35606                 this.files.push(file);
35607             }
35608         }, this);
35609         
35610         this.queue();
35611         
35612     },
35613     
35614     queue : function()
35615     {
35616         this.selectorEl.dom.value = '';
35617         
35618         if(!this.files || !this.files.length){
35619             return;
35620         }
35621         
35622         if(this.boxes > 0 && this.files.length > this.boxes){
35623             this.files = this.files.slice(0, this.boxes);
35624         }
35625         
35626         this.uploader.show();
35627         
35628         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35629             this.uploader.hide();
35630         }
35631         
35632         var _this = this;
35633         
35634         var files = [];
35635         
35636         var docs = [];
35637         
35638         Roo.each(this.files, function(file){
35639             
35640             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35641                 var f = this.renderPreview(file);
35642                 files.push(f);
35643                 return;
35644             }
35645             
35646             if(file.type.indexOf('image') != -1){
35647                 this.delegates.push(
35648                     (function(){
35649                         _this.process(file);
35650                     }).createDelegate(this)
35651                 );
35652         
35653                 return;
35654             }
35655             
35656             docs.push(
35657                 (function(){
35658                     _this.process(file);
35659                 }).createDelegate(this)
35660             );
35661             
35662         }, this);
35663         
35664         this.files = files;
35665         
35666         this.delegates = this.delegates.concat(docs);
35667         
35668         if(!this.delegates.length){
35669             this.refresh();
35670             return;
35671         }
35672         
35673         this.progressBar.aria_valuemax = this.delegates.length;
35674         
35675         this.arrange();
35676         
35677         return;
35678     },
35679     
35680     arrange : function()
35681     {
35682         if(!this.delegates.length){
35683             this.progressDialog.hide();
35684             this.refresh();
35685             return;
35686         }
35687         
35688         var delegate = this.delegates.shift();
35689         
35690         this.progressDialog.show();
35691         
35692         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
35693         
35694         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
35695         
35696         delegate();
35697     },
35698     
35699     refresh : function()
35700     {
35701         this.uploader.show();
35702         
35703         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35704             this.uploader.hide();
35705         }
35706         
35707         Roo.isTouch ? this.closable(false) : this.closable(true);
35708         
35709         this.fireEvent('refresh', this);
35710     },
35711     
35712     onRemove : function(e, el, o)
35713     {
35714         e.preventDefault();
35715         
35716         this.fireEvent('remove', this, o);
35717         
35718     },
35719     
35720     remove : function(o)
35721     {
35722         var files = [];
35723         
35724         Roo.each(this.files, function(file){
35725             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
35726                 files.push(file);
35727                 return;
35728             }
35729
35730             o.target.remove();
35731
35732         }, this);
35733         
35734         this.files = files;
35735         
35736         this.refresh();
35737     },
35738     
35739     clear : function()
35740     {
35741         Roo.each(this.files, function(file){
35742             if(!file.target){
35743                 return;
35744             }
35745             
35746             file.target.remove();
35747
35748         }, this);
35749         
35750         this.files = [];
35751         
35752         this.refresh();
35753     },
35754     
35755     onClick : function(e, el, o)
35756     {
35757         e.preventDefault();
35758         
35759         this.fireEvent('click', this, o);
35760         
35761     },
35762     
35763     closable : function(closable)
35764     {
35765         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
35766             
35767             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35768             
35769             if(closable){
35770                 el.show();
35771                 return;
35772             }
35773             
35774             el.hide();
35775             
35776         }, this);
35777     },
35778     
35779     xhrOnLoad : function(xhr)
35780     {
35781         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35782             el.remove();
35783         }, this);
35784         
35785         if (xhr.readyState !== 4) {
35786             this.arrange();
35787             this.fireEvent('exception', this, xhr);
35788             return;
35789         }
35790
35791         var response = Roo.decode(xhr.responseText);
35792         
35793         if(!response.success){
35794             this.arrange();
35795             this.fireEvent('exception', this, xhr);
35796             return;
35797         }
35798         
35799         var file = this.renderPreview(response.data);
35800         
35801         this.files.push(file);
35802         
35803         this.arrange();
35804         
35805         this.fireEvent('afterupload', this, xhr);
35806         
35807     },
35808     
35809     xhrOnError : function(xhr)
35810     {
35811         Roo.log('xhr on error');
35812         
35813         var response = Roo.decode(xhr.responseText);
35814           
35815         Roo.log(response);
35816         
35817         this.arrange();
35818     },
35819     
35820     process : function(file)
35821     {
35822         if(this.fireEvent('process', this, file) !== false){
35823             if(this.editable && file.type.indexOf('image') != -1){
35824                 this.fireEvent('edit', this, file);
35825                 return;
35826             }
35827
35828             this.uploadStart(file, false);
35829
35830             return;
35831         }
35832         
35833     },
35834     
35835     uploadStart : function(file, crop)
35836     {
35837         this.xhr = new XMLHttpRequest();
35838         
35839         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35840             this.arrange();
35841             return;
35842         }
35843         
35844         file.xhr = this.xhr;
35845             
35846         this.managerEl.createChild({
35847             tag : 'div',
35848             cls : 'roo-document-manager-loading',
35849             cn : [
35850                 {
35851                     tag : 'div',
35852                     tooltip : file.name,
35853                     cls : 'roo-document-manager-thumb',
35854                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35855                 }
35856             ]
35857
35858         });
35859
35860         this.xhr.open(this.method, this.url, true);
35861         
35862         var headers = {
35863             "Accept": "application/json",
35864             "Cache-Control": "no-cache",
35865             "X-Requested-With": "XMLHttpRequest"
35866         };
35867         
35868         for (var headerName in headers) {
35869             var headerValue = headers[headerName];
35870             if (headerValue) {
35871                 this.xhr.setRequestHeader(headerName, headerValue);
35872             }
35873         }
35874         
35875         var _this = this;
35876         
35877         this.xhr.onload = function()
35878         {
35879             _this.xhrOnLoad(_this.xhr);
35880         }
35881         
35882         this.xhr.onerror = function()
35883         {
35884             _this.xhrOnError(_this.xhr);
35885         }
35886         
35887         var formData = new FormData();
35888
35889         formData.append('returnHTML', 'NO');
35890         
35891         if(crop){
35892             formData.append('crop', crop);
35893         }
35894         
35895         formData.append(this.paramName, file, file.name);
35896         
35897         var options = {
35898             file : file, 
35899             manually : false
35900         };
35901         
35902         if(this.fireEvent('prepare', this, formData, options) != false){
35903             
35904             if(options.manually){
35905                 return;
35906             }
35907             
35908             this.xhr.send(formData);
35909             return;
35910         };
35911         
35912         this.uploadCancel();
35913     },
35914     
35915     uploadCancel : function()
35916     {
35917         if (this.xhr) {
35918             this.xhr.abort();
35919         }
35920         
35921         this.delegates = [];
35922         
35923         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35924             el.remove();
35925         }, this);
35926         
35927         this.arrange();
35928     },
35929     
35930     renderPreview : function(file)
35931     {
35932         if(typeof(file.target) != 'undefined' && file.target){
35933             return file;
35934         }
35935         
35936         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
35937         
35938         var previewEl = this.managerEl.createChild({
35939             tag : 'div',
35940             cls : 'roo-document-manager-preview',
35941             cn : [
35942                 {
35943                     tag : 'div',
35944                     tooltip : file[this.toolTipName],
35945                     cls : 'roo-document-manager-thumb',
35946                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
35947                 },
35948                 {
35949                     tag : 'button',
35950                     cls : 'close',
35951                     html : '<i class="fa fa-times-circle"></i>'
35952                 }
35953             ]
35954         });
35955
35956         var close = previewEl.select('button.close', true).first();
35957
35958         close.on('click', this.onRemove, this, file);
35959
35960         file.target = previewEl;
35961
35962         var image = previewEl.select('img', true).first();
35963         
35964         var _this = this;
35965         
35966         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
35967         
35968         image.on('click', this.onClick, this, file);
35969         
35970         this.fireEvent('previewrendered', this, file);
35971         
35972         return file;
35973         
35974     },
35975     
35976     onPreviewLoad : function(file, image)
35977     {
35978         if(typeof(file.target) == 'undefined' || !file.target){
35979             return;
35980         }
35981         
35982         var width = image.dom.naturalWidth || image.dom.width;
35983         var height = image.dom.naturalHeight || image.dom.height;
35984         
35985         if(!this.previewResize) {
35986             return;
35987         }
35988         
35989         if(width > height){
35990             file.target.addClass('wide');
35991             return;
35992         }
35993         
35994         file.target.addClass('tall');
35995         return;
35996         
35997     },
35998     
35999     uploadFromSource : function(file, crop)
36000     {
36001         this.xhr = new XMLHttpRequest();
36002         
36003         this.managerEl.createChild({
36004             tag : 'div',
36005             cls : 'roo-document-manager-loading',
36006             cn : [
36007                 {
36008                     tag : 'div',
36009                     tooltip : file.name,
36010                     cls : 'roo-document-manager-thumb',
36011                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
36012                 }
36013             ]
36014
36015         });
36016
36017         this.xhr.open(this.method, this.url, true);
36018         
36019         var headers = {
36020             "Accept": "application/json",
36021             "Cache-Control": "no-cache",
36022             "X-Requested-With": "XMLHttpRequest"
36023         };
36024         
36025         for (var headerName in headers) {
36026             var headerValue = headers[headerName];
36027             if (headerValue) {
36028                 this.xhr.setRequestHeader(headerName, headerValue);
36029             }
36030         }
36031         
36032         var _this = this;
36033         
36034         this.xhr.onload = function()
36035         {
36036             _this.xhrOnLoad(_this.xhr);
36037         }
36038         
36039         this.xhr.onerror = function()
36040         {
36041             _this.xhrOnError(_this.xhr);
36042         }
36043         
36044         var formData = new FormData();
36045
36046         formData.append('returnHTML', 'NO');
36047         
36048         formData.append('crop', crop);
36049         
36050         if(typeof(file.filename) != 'undefined'){
36051             formData.append('filename', file.filename);
36052         }
36053         
36054         if(typeof(file.mimetype) != 'undefined'){
36055             formData.append('mimetype', file.mimetype);
36056         }
36057         
36058         Roo.log(formData);
36059         
36060         if(this.fireEvent('prepare', this, formData) != false){
36061             this.xhr.send(formData);
36062         };
36063     }
36064 });
36065
36066 /*
36067 * Licence: LGPL
36068 */
36069
36070 /**
36071  * @class Roo.bootstrap.DocumentViewer
36072  * @extends Roo.bootstrap.Component
36073  * Bootstrap DocumentViewer class
36074  * @cfg {Boolean} showDownload (true|false) show download button (default true)
36075  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
36076  * 
36077  * @constructor
36078  * Create a new DocumentViewer
36079  * @param {Object} config The config object
36080  */
36081
36082 Roo.bootstrap.DocumentViewer = function(config){
36083     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
36084     
36085     this.addEvents({
36086         /**
36087          * @event initial
36088          * Fire after initEvent
36089          * @param {Roo.bootstrap.DocumentViewer} this
36090          */
36091         "initial" : true,
36092         /**
36093          * @event click
36094          * Fire after click
36095          * @param {Roo.bootstrap.DocumentViewer} this
36096          */
36097         "click" : true,
36098         /**
36099          * @event download
36100          * Fire after download button
36101          * @param {Roo.bootstrap.DocumentViewer} this
36102          */
36103         "download" : true,
36104         /**
36105          * @event trash
36106          * Fire after trash button
36107          * @param {Roo.bootstrap.DocumentViewer} this
36108          */
36109         "trash" : true
36110         
36111     });
36112 };
36113
36114 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
36115     
36116     showDownload : true,
36117     
36118     showTrash : true,
36119     
36120     getAutoCreate : function()
36121     {
36122         var cfg = {
36123             tag : 'div',
36124             cls : 'roo-document-viewer',
36125             cn : [
36126                 {
36127                     tag : 'div',
36128                     cls : 'roo-document-viewer-body',
36129                     cn : [
36130                         {
36131                             tag : 'div',
36132                             cls : 'roo-document-viewer-thumb',
36133                             cn : [
36134                                 {
36135                                     tag : 'img',
36136                                     cls : 'roo-document-viewer-image'
36137                                 }
36138                             ]
36139                         }
36140                     ]
36141                 },
36142                 {
36143                     tag : 'div',
36144                     cls : 'roo-document-viewer-footer',
36145                     cn : {
36146                         tag : 'div',
36147                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
36148                         cn : [
36149                             {
36150                                 tag : 'div',
36151                                 cls : 'btn-group roo-document-viewer-download',
36152                                 cn : [
36153                                     {
36154                                         tag : 'button',
36155                                         cls : 'btn btn-default',
36156                                         html : '<i class="fa fa-download"></i>'
36157                                     }
36158                                 ]
36159                             },
36160                             {
36161                                 tag : 'div',
36162                                 cls : 'btn-group roo-document-viewer-trash',
36163                                 cn : [
36164                                     {
36165                                         tag : 'button',
36166                                         cls : 'btn btn-default',
36167                                         html : '<i class="fa fa-trash"></i>'
36168                                     }
36169                                 ]
36170                             }
36171                         ]
36172                     }
36173                 }
36174             ]
36175         };
36176         
36177         return cfg;
36178     },
36179     
36180     initEvents : function()
36181     {
36182         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
36183         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36184         
36185         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
36186         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36187         
36188         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
36189         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36190         
36191         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
36192         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
36193         
36194         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
36195         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
36196         
36197         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
36198         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
36199         
36200         this.bodyEl.on('click', this.onClick, this);
36201         this.downloadBtn.on('click', this.onDownload, this);
36202         this.trashBtn.on('click', this.onTrash, this);
36203         
36204         this.downloadBtn.hide();
36205         this.trashBtn.hide();
36206         
36207         if(this.showDownload){
36208             this.downloadBtn.show();
36209         }
36210         
36211         if(this.showTrash){
36212             this.trashBtn.show();
36213         }
36214         
36215         if(!this.showDownload && !this.showTrash) {
36216             this.footerEl.hide();
36217         }
36218         
36219     },
36220     
36221     initial : function()
36222     {
36223         this.fireEvent('initial', this);
36224         
36225     },
36226     
36227     onClick : function(e)
36228     {
36229         e.preventDefault();
36230         
36231         this.fireEvent('click', this);
36232     },
36233     
36234     onDownload : function(e)
36235     {
36236         e.preventDefault();
36237         
36238         this.fireEvent('download', this);
36239     },
36240     
36241     onTrash : function(e)
36242     {
36243         e.preventDefault();
36244         
36245         this.fireEvent('trash', this);
36246     }
36247     
36248 });
36249 /*
36250  * - LGPL
36251  *
36252  * FieldLabel
36253  * 
36254  */
36255
36256 /**
36257  * @class Roo.bootstrap.form.FieldLabel
36258  * @extends Roo.bootstrap.Component
36259  * Bootstrap FieldLabel class
36260  * @cfg {String} html contents of the element
36261  * @cfg {String} tag tag of the element default label
36262  * @cfg {String} cls class of the element
36263  * @cfg {String} target label target 
36264  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
36265  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
36266  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
36267  * @cfg {String} iconTooltip default "This field is required"
36268  * @cfg {String} indicatorpos (left|right) default left
36269  * 
36270  * @constructor
36271  * Create a new FieldLabel
36272  * @param {Object} config The config object
36273  */
36274
36275 Roo.bootstrap.form.FieldLabel = function(config){
36276     Roo.bootstrap.Element.superclass.constructor.call(this, config);
36277     
36278     this.addEvents({
36279             /**
36280              * @event invalid
36281              * Fires after the field has been marked as invalid.
36282              * @param {Roo.form.FieldLabel} this
36283              * @param {String} msg The validation message
36284              */
36285             invalid : true,
36286             /**
36287              * @event valid
36288              * Fires after the field has been validated with no errors.
36289              * @param {Roo.form.FieldLabel} this
36290              */
36291             valid : true
36292         });
36293 };
36294
36295 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
36296     
36297     tag: 'label',
36298     cls: '',
36299     html: '',
36300     target: '',
36301     allowBlank : true,
36302     invalidClass : 'has-warning',
36303     validClass : 'has-success',
36304     iconTooltip : 'This field is required',
36305     indicatorpos : 'left',
36306     
36307     getAutoCreate : function(){
36308         
36309         var cls = "";
36310         if (!this.allowBlank) {
36311             cls  = "visible";
36312         }
36313         
36314         var cfg = {
36315             tag : this.tag,
36316             cls : 'roo-bootstrap-field-label ' + this.cls,
36317             for : this.target,
36318             cn : [
36319                 {
36320                     tag : 'i',
36321                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
36322                     tooltip : this.iconTooltip
36323                 },
36324                 {
36325                     tag : 'span',
36326                     html : this.html
36327                 }
36328             ] 
36329         };
36330         
36331         if(this.indicatorpos == 'right'){
36332             var cfg = {
36333                 tag : this.tag,
36334                 cls : 'roo-bootstrap-field-label ' + this.cls,
36335                 for : this.target,
36336                 cn : [
36337                     {
36338                         tag : 'span',
36339                         html : this.html
36340                     },
36341                     {
36342                         tag : 'i',
36343                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
36344                         tooltip : this.iconTooltip
36345                     }
36346                 ] 
36347             };
36348         }
36349         
36350         return cfg;
36351     },
36352     
36353     initEvents: function() 
36354     {
36355         Roo.bootstrap.Element.superclass.initEvents.call(this);
36356         
36357         this.indicator = this.indicatorEl();
36358         
36359         if(this.indicator){
36360             this.indicator.removeClass('visible');
36361             this.indicator.addClass('invisible');
36362         }
36363         
36364         Roo.bootstrap.form.FieldLabel.register(this);
36365     },
36366     
36367     indicatorEl : function()
36368     {
36369         var indicator = this.el.select('i.roo-required-indicator',true).first();
36370         
36371         if(!indicator){
36372             return false;
36373         }
36374         
36375         return indicator;
36376         
36377     },
36378     
36379     /**
36380      * Mark this field as valid
36381      */
36382     markValid : function()
36383     {
36384         if(this.indicator){
36385             this.indicator.removeClass('visible');
36386             this.indicator.addClass('invisible');
36387         }
36388         if (Roo.bootstrap.version == 3) {
36389             this.el.removeClass(this.invalidClass);
36390             this.el.addClass(this.validClass);
36391         } else {
36392             this.el.removeClass('is-invalid');
36393             this.el.addClass('is-valid');
36394         }
36395         
36396         
36397         this.fireEvent('valid', this);
36398     },
36399     
36400     /**
36401      * Mark this field as invalid
36402      * @param {String} msg The validation message
36403      */
36404     markInvalid : function(msg)
36405     {
36406         if(this.indicator){
36407             this.indicator.removeClass('invisible');
36408             this.indicator.addClass('visible');
36409         }
36410           if (Roo.bootstrap.version == 3) {
36411             this.el.removeClass(this.validClass);
36412             this.el.addClass(this.invalidClass);
36413         } else {
36414             this.el.removeClass('is-valid');
36415             this.el.addClass('is-invalid');
36416         }
36417         
36418         
36419         this.fireEvent('invalid', this, msg);
36420     }
36421     
36422    
36423 });
36424
36425 Roo.apply(Roo.bootstrap.form.FieldLabel, {
36426     
36427     groups: {},
36428     
36429      /**
36430     * register a FieldLabel Group
36431     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
36432     */
36433     register : function(label)
36434     {
36435         if(this.groups.hasOwnProperty(label.target)){
36436             return;
36437         }
36438      
36439         this.groups[label.target] = label;
36440         
36441     },
36442     /**
36443     * fetch a FieldLabel Group based on the target
36444     * @param {string} target
36445     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
36446     */
36447     get: function(target) {
36448         if (typeof(this.groups[target]) == 'undefined') {
36449             return false;
36450         }
36451         
36452         return this.groups[target] ;
36453     }
36454 });
36455
36456  
36457
36458  /*
36459  * - LGPL
36460  *
36461  * page DateSplitField.
36462  * 
36463  */
36464
36465
36466 /**
36467  * @class Roo.bootstrap.form.DateSplitField
36468  * @extends Roo.bootstrap.Component
36469  * Bootstrap DateSplitField class
36470  * @cfg {string} fieldLabel - the label associated
36471  * @cfg {Number} labelWidth set the width of label (0-12)
36472  * @cfg {String} labelAlign (top|left)
36473  * @cfg {Boolean} dayAllowBlank (true|false) default false
36474  * @cfg {Boolean} monthAllowBlank (true|false) default false
36475  * @cfg {Boolean} yearAllowBlank (true|false) default false
36476  * @cfg {string} dayPlaceholder 
36477  * @cfg {string} monthPlaceholder
36478  * @cfg {string} yearPlaceholder
36479  * @cfg {string} dayFormat default 'd'
36480  * @cfg {string} monthFormat default 'm'
36481  * @cfg {string} yearFormat default 'Y'
36482  * @cfg {Number} labellg set the width of label (1-12)
36483  * @cfg {Number} labelmd set the width of label (1-12)
36484  * @cfg {Number} labelsm set the width of label (1-12)
36485  * @cfg {Number} labelxs set the width of label (1-12)
36486
36487  *     
36488  * @constructor
36489  * Create a new DateSplitField
36490  * @param {Object} config The config object
36491  */
36492
36493 Roo.bootstrap.form.DateSplitField = function(config){
36494     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
36495     
36496     this.addEvents({
36497         // raw events
36498          /**
36499          * @event years
36500          * getting the data of years
36501          * @param {Roo.bootstrap.form.DateSplitField} this
36502          * @param {Object} years
36503          */
36504         "years" : true,
36505         /**
36506          * @event days
36507          * getting the data of days
36508          * @param {Roo.bootstrap.form.DateSplitField} this
36509          * @param {Object} days
36510          */
36511         "days" : true,
36512         /**
36513          * @event invalid
36514          * Fires after the field has been marked as invalid.
36515          * @param {Roo.form.Field} this
36516          * @param {String} msg The validation message
36517          */
36518         invalid : true,
36519        /**
36520          * @event valid
36521          * Fires after the field has been validated with no errors.
36522          * @param {Roo.form.Field} this
36523          */
36524         valid : true
36525     });
36526 };
36527
36528 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
36529     
36530     fieldLabel : '',
36531     labelAlign : 'top',
36532     labelWidth : 3,
36533     dayAllowBlank : false,
36534     monthAllowBlank : false,
36535     yearAllowBlank : false,
36536     dayPlaceholder : '',
36537     monthPlaceholder : '',
36538     yearPlaceholder : '',
36539     dayFormat : 'd',
36540     monthFormat : 'm',
36541     yearFormat : 'Y',
36542     isFormField : true,
36543     labellg : 0,
36544     labelmd : 0,
36545     labelsm : 0,
36546     labelxs : 0,
36547     
36548     getAutoCreate : function()
36549     {
36550         var cfg = {
36551             tag : 'div',
36552             cls : 'row roo-date-split-field-group',
36553             cn : [
36554                 {
36555                     tag : 'input',
36556                     type : 'hidden',
36557                     cls : 'form-hidden-field roo-date-split-field-group-value',
36558                     name : this.name
36559                 }
36560             ]
36561         };
36562         
36563         var labelCls = 'col-md-12';
36564         var contentCls = 'col-md-4';
36565         
36566         if(this.fieldLabel){
36567             
36568             var label = {
36569                 tag : 'div',
36570                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
36571                 cn : [
36572                     {
36573                         tag : 'label',
36574                         html : this.fieldLabel
36575                     }
36576                 ]
36577             };
36578             
36579             if(this.labelAlign == 'left'){
36580             
36581                 if(this.labelWidth > 12){
36582                     label.style = "width: " + this.labelWidth + 'px';
36583                 }
36584
36585                 if(this.labelWidth < 13 && this.labelmd == 0){
36586                     this.labelmd = this.labelWidth;
36587                 }
36588
36589                 if(this.labellg > 0){
36590                     labelCls = ' col-lg-' + this.labellg;
36591                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
36592                 }
36593
36594                 if(this.labelmd > 0){
36595                     labelCls = ' col-md-' + this.labelmd;
36596                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
36597                 }
36598
36599                 if(this.labelsm > 0){
36600                     labelCls = ' col-sm-' + this.labelsm;
36601                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
36602                 }
36603
36604                 if(this.labelxs > 0){
36605                     labelCls = ' col-xs-' + this.labelxs;
36606                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
36607                 }
36608             }
36609             
36610             label.cls += ' ' + labelCls;
36611             
36612             cfg.cn.push(label);
36613         }
36614         
36615         Roo.each(['day', 'month', 'year'], function(t){
36616             cfg.cn.push({
36617                 tag : 'div',
36618                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
36619             });
36620         }, this);
36621         
36622         return cfg;
36623     },
36624     
36625     inputEl: function ()
36626     {
36627         return this.el.select('.roo-date-split-field-group-value', true).first();
36628     },
36629     
36630     onRender : function(ct, position) 
36631     {
36632         var _this = this;
36633         
36634         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
36635         
36636         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
36637         
36638         this.dayField = new Roo.bootstrap.form.ComboBox({
36639             allowBlank : this.dayAllowBlank,
36640             alwaysQuery : true,
36641             displayField : 'value',
36642             editable : false,
36643             fieldLabel : '',
36644             forceSelection : true,
36645             mode : 'local',
36646             placeholder : this.dayPlaceholder,
36647             selectOnFocus : true,
36648             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36649             triggerAction : 'all',
36650             typeAhead : true,
36651             valueField : 'value',
36652             store : new Roo.data.SimpleStore({
36653                 data : (function() {    
36654                     var days = [];
36655                     _this.fireEvent('days', _this, days);
36656                     return days;
36657                 })(),
36658                 fields : [ 'value' ]
36659             }),
36660             listeners : {
36661                 select : function (_self, record, index)
36662                 {
36663                     _this.setValue(_this.getValue());
36664                 }
36665             }
36666         });
36667
36668         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
36669         
36670         this.monthField = new Roo.bootstrap.form.MonthField({
36671             after : '<i class=\"fa fa-calendar\"></i>',
36672             allowBlank : this.monthAllowBlank,
36673             placeholder : this.monthPlaceholder,
36674             readOnly : true,
36675             listeners : {
36676                 render : function (_self)
36677                 {
36678                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
36679                         e.preventDefault();
36680                         _self.focus();
36681                     });
36682                 },
36683                 select : function (_self, oldvalue, newvalue)
36684                 {
36685                     _this.setValue(_this.getValue());
36686                 }
36687             }
36688         });
36689         
36690         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
36691         
36692         this.yearField = new Roo.bootstrap.form.ComboBox({
36693             allowBlank : this.yearAllowBlank,
36694             alwaysQuery : true,
36695             displayField : 'value',
36696             editable : false,
36697             fieldLabel : '',
36698             forceSelection : true,
36699             mode : 'local',
36700             placeholder : this.yearPlaceholder,
36701             selectOnFocus : true,
36702             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36703             triggerAction : 'all',
36704             typeAhead : true,
36705             valueField : 'value',
36706             store : new Roo.data.SimpleStore({
36707                 data : (function() {
36708                     var years = [];
36709                     _this.fireEvent('years', _this, years);
36710                     return years;
36711                 })(),
36712                 fields : [ 'value' ]
36713             }),
36714             listeners : {
36715                 select : function (_self, record, index)
36716                 {
36717                     _this.setValue(_this.getValue());
36718                 }
36719             }
36720         });
36721
36722         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
36723     },
36724     
36725     setValue : function(v, format)
36726     {
36727         this.inputEl.dom.value = v;
36728         
36729         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
36730         
36731         var d = Date.parseDate(v, f);
36732         
36733         if(!d){
36734             this.validate();
36735             return;
36736         }
36737         
36738         this.setDay(d.format(this.dayFormat));
36739         this.setMonth(d.format(this.monthFormat));
36740         this.setYear(d.format(this.yearFormat));
36741         
36742         this.validate();
36743         
36744         return;
36745     },
36746     
36747     setDay : function(v)
36748     {
36749         this.dayField.setValue(v);
36750         this.inputEl.dom.value = this.getValue();
36751         this.validate();
36752         return;
36753     },
36754     
36755     setMonth : function(v)
36756     {
36757         this.monthField.setValue(v, true);
36758         this.inputEl.dom.value = this.getValue();
36759         this.validate();
36760         return;
36761     },
36762     
36763     setYear : function(v)
36764     {
36765         this.yearField.setValue(v);
36766         this.inputEl.dom.value = this.getValue();
36767         this.validate();
36768         return;
36769     },
36770     
36771     getDay : function()
36772     {
36773         return this.dayField.getValue();
36774     },
36775     
36776     getMonth : function()
36777     {
36778         return this.monthField.getValue();
36779     },
36780     
36781     getYear : function()
36782     {
36783         return this.yearField.getValue();
36784     },
36785     
36786     getValue : function()
36787     {
36788         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
36789         
36790         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
36791         
36792         return date;
36793     },
36794     
36795     reset : function()
36796     {
36797         this.setDay('');
36798         this.setMonth('');
36799         this.setYear('');
36800         this.inputEl.dom.value = '';
36801         this.validate();
36802         return;
36803     },
36804     
36805     validate : function()
36806     {
36807         var d = this.dayField.validate();
36808         var m = this.monthField.validate();
36809         var y = this.yearField.validate();
36810         
36811         var valid = true;
36812         
36813         if(
36814                 (!this.dayAllowBlank && !d) ||
36815                 (!this.monthAllowBlank && !m) ||
36816                 (!this.yearAllowBlank && !y)
36817         ){
36818             valid = false;
36819         }
36820         
36821         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
36822             return valid;
36823         }
36824         
36825         if(valid){
36826             this.markValid();
36827             return valid;
36828         }
36829         
36830         this.markInvalid();
36831         
36832         return valid;
36833     },
36834     
36835     markValid : function()
36836     {
36837         
36838         var label = this.el.select('label', true).first();
36839         var icon = this.el.select('i.fa-star', true).first();
36840
36841         if(label && icon){
36842             icon.remove();
36843         }
36844         
36845         this.fireEvent('valid', this);
36846     },
36847     
36848      /**
36849      * Mark this field as invalid
36850      * @param {String} msg The validation message
36851      */
36852     markInvalid : function(msg)
36853     {
36854         
36855         var label = this.el.select('label', true).first();
36856         var icon = this.el.select('i.fa-star', true).first();
36857
36858         if(label && !icon){
36859             this.el.select('.roo-date-split-field-label', true).createChild({
36860                 tag : 'i',
36861                 cls : 'text-danger fa fa-lg fa-star',
36862                 tooltip : 'This field is required',
36863                 style : 'margin-right:5px;'
36864             }, label, true);
36865         }
36866         
36867         this.fireEvent('invalid', this, msg);
36868     },
36869     
36870     clearInvalid : function()
36871     {
36872         var label = this.el.select('label', true).first();
36873         var icon = this.el.select('i.fa-star', true).first();
36874
36875         if(label && icon){
36876             icon.remove();
36877         }
36878         
36879         this.fireEvent('valid', this);
36880     },
36881     
36882     getName: function()
36883     {
36884         return this.name;
36885     }
36886     
36887 });
36888
36889  
36890
36891 /**
36892  * @class Roo.bootstrap.LayoutMasonry
36893  * @extends Roo.bootstrap.Component
36894  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
36895  * Bootstrap Layout Masonry class
36896  *
36897  * This is based on 
36898  * http://masonry.desandro.com
36899  *
36900  * The idea is to render all the bricks based on vertical width...
36901  *
36902  * The original code extends 'outlayer' - we might need to use that....
36903
36904  * @constructor
36905  * Create a new Element
36906  * @param {Object} config The config object
36907  */
36908
36909 Roo.bootstrap.LayoutMasonry = function(config){
36910     
36911     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
36912     
36913     this.bricks = [];
36914     
36915     Roo.bootstrap.LayoutMasonry.register(this);
36916     
36917     this.addEvents({
36918         // raw events
36919         /**
36920          * @event layout
36921          * Fire after layout the items
36922          * @param {Roo.bootstrap.LayoutMasonry} this
36923          * @param {Roo.EventObject} e
36924          */
36925         "layout" : true
36926     });
36927     
36928 };
36929
36930 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
36931     
36932     /**
36933      * @cfg {Boolean} isLayoutInstant = no animation?
36934      */   
36935     isLayoutInstant : false, // needed?
36936    
36937     /**
36938      * @cfg {Number} boxWidth  width of the columns
36939      */   
36940     boxWidth : 450,
36941     
36942       /**
36943      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
36944      */   
36945     boxHeight : 0,
36946     
36947     /**
36948      * @cfg {Number} padWidth padding below box..
36949      */   
36950     padWidth : 10, 
36951     
36952     /**
36953      * @cfg {Number} gutter gutter width..
36954      */   
36955     gutter : 10,
36956     
36957      /**
36958      * @cfg {Number} maxCols maximum number of columns
36959      */   
36960     
36961     maxCols: 0,
36962     
36963     /**
36964      * @cfg {Boolean} isAutoInitial defalut true
36965      */   
36966     isAutoInitial : true, 
36967     
36968     containerWidth: 0,
36969     
36970     /**
36971      * @cfg {Boolean} isHorizontal defalut false
36972      */   
36973     isHorizontal : false, 
36974
36975     currentSize : null,
36976     
36977     tag: 'div',
36978     
36979     cls: '',
36980     
36981     bricks: null, //CompositeElement
36982     
36983     cols : 1,
36984     
36985     _isLayoutInited : false,
36986     
36987 //    isAlternative : false, // only use for vertical layout...
36988     
36989     /**
36990      * @cfg {Number} alternativePadWidth padding below box..
36991      */   
36992     alternativePadWidth : 50,
36993     
36994     selectedBrick : [],
36995     
36996     getAutoCreate : function(){
36997         
36998         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
36999         
37000         var cfg = {
37001             tag: this.tag,
37002             cls: 'blog-masonary-wrapper ' + this.cls,
37003             cn : {
37004                 cls : 'mas-boxes masonary'
37005             }
37006         };
37007         
37008         return cfg;
37009     },
37010     
37011     getChildContainer: function( )
37012     {
37013         if (this.boxesEl) {
37014             return this.boxesEl;
37015         }
37016         
37017         this.boxesEl = this.el.select('.mas-boxes').first();
37018         
37019         return this.boxesEl;
37020     },
37021     
37022     
37023     initEvents : function()
37024     {
37025         var _this = this;
37026         
37027         if(this.isAutoInitial){
37028             Roo.log('hook children rendered');
37029             this.on('childrenrendered', function() {
37030                 Roo.log('children rendered');
37031                 _this.initial();
37032             } ,this);
37033         }
37034     },
37035     
37036     initial : function()
37037     {
37038         this.selectedBrick = [];
37039         
37040         this.currentSize = this.el.getBox(true);
37041         
37042         Roo.EventManager.onWindowResize(this.resize, this); 
37043
37044         if(!this.isAutoInitial){
37045             this.layout();
37046             return;
37047         }
37048         
37049         this.layout();
37050         
37051         return;
37052         //this.layout.defer(500,this);
37053         
37054     },
37055     
37056     resize : function()
37057     {
37058         var cs = this.el.getBox(true);
37059         
37060         if (
37061                 this.currentSize.width == cs.width && 
37062                 this.currentSize.x == cs.x && 
37063                 this.currentSize.height == cs.height && 
37064                 this.currentSize.y == cs.y 
37065         ) {
37066             Roo.log("no change in with or X or Y");
37067             return;
37068         }
37069         
37070         this.currentSize = cs;
37071         
37072         this.layout();
37073         
37074     },
37075     
37076     layout : function()
37077     {   
37078         this._resetLayout();
37079         
37080         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
37081         
37082         this.layoutItems( isInstant );
37083       
37084         this._isLayoutInited = true;
37085         
37086         this.fireEvent('layout', this);
37087         
37088     },
37089     
37090     _resetLayout : function()
37091     {
37092         if(this.isHorizontal){
37093             this.horizontalMeasureColumns();
37094             return;
37095         }
37096         
37097         this.verticalMeasureColumns();
37098         
37099     },
37100     
37101     verticalMeasureColumns : function()
37102     {
37103         this.getContainerWidth();
37104         
37105 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
37106 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
37107 //            return;
37108 //        }
37109         
37110         var boxWidth = this.boxWidth + this.padWidth;
37111         
37112         if(this.containerWidth < this.boxWidth){
37113             boxWidth = this.containerWidth
37114         }
37115         
37116         var containerWidth = this.containerWidth;
37117         
37118         var cols = Math.floor(containerWidth / boxWidth);
37119         
37120         this.cols = Math.max( cols, 1 );
37121         
37122         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
37123         
37124         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
37125         
37126         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
37127         
37128         this.colWidth = boxWidth + avail - this.padWidth;
37129         
37130         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
37131         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
37132     },
37133     
37134     horizontalMeasureColumns : function()
37135     {
37136         this.getContainerWidth();
37137         
37138         var boxWidth = this.boxWidth;
37139         
37140         if(this.containerWidth < boxWidth){
37141             boxWidth = this.containerWidth;
37142         }
37143         
37144         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
37145         
37146         this.el.setHeight(boxWidth);
37147         
37148     },
37149     
37150     getContainerWidth : function()
37151     {
37152         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
37153     },
37154     
37155     layoutItems : function( isInstant )
37156     {
37157         Roo.log(this.bricks);
37158         
37159         var items = Roo.apply([], this.bricks);
37160         
37161         if(this.isHorizontal){
37162             this._horizontalLayoutItems( items , isInstant );
37163             return;
37164         }
37165         
37166 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
37167 //            this._verticalAlternativeLayoutItems( items , isInstant );
37168 //            return;
37169 //        }
37170         
37171         this._verticalLayoutItems( items , isInstant );
37172         
37173     },
37174     
37175     _verticalLayoutItems : function ( items , isInstant)
37176     {
37177         if ( !items || !items.length ) {
37178             return;
37179         }
37180         
37181         var standard = [
37182             ['xs', 'xs', 'xs', 'tall'],
37183             ['xs', 'xs', 'tall'],
37184             ['xs', 'xs', 'sm'],
37185             ['xs', 'xs', 'xs'],
37186             ['xs', 'tall'],
37187             ['xs', 'sm'],
37188             ['xs', 'xs'],
37189             ['xs'],
37190             
37191             ['sm', 'xs', 'xs'],
37192             ['sm', 'xs'],
37193             ['sm'],
37194             
37195             ['tall', 'xs', 'xs', 'xs'],
37196             ['tall', 'xs', 'xs'],
37197             ['tall', 'xs'],
37198             ['tall']
37199             
37200         ];
37201         
37202         var queue = [];
37203         
37204         var boxes = [];
37205         
37206         var box = [];
37207         
37208         Roo.each(items, function(item, k){
37209             
37210             switch (item.size) {
37211                 // these layouts take up a full box,
37212                 case 'md' :
37213                 case 'md-left' :
37214                 case 'md-right' :
37215                 case 'wide' :
37216                     
37217                     if(box.length){
37218                         boxes.push(box);
37219                         box = [];
37220                     }
37221                     
37222                     boxes.push([item]);
37223                     
37224                     break;
37225                     
37226                 case 'xs' :
37227                 case 'sm' :
37228                 case 'tall' :
37229                     
37230                     box.push(item);
37231                     
37232                     break;
37233                 default :
37234                     break;
37235                     
37236             }
37237             
37238         }, this);
37239         
37240         if(box.length){
37241             boxes.push(box);
37242             box = [];
37243         }
37244         
37245         var filterPattern = function(box, length)
37246         {
37247             if(!box.length){
37248                 return;
37249             }
37250             
37251             var match = false;
37252             
37253             var pattern = box.slice(0, length);
37254             
37255             var format = [];
37256             
37257             Roo.each(pattern, function(i){
37258                 format.push(i.size);
37259             }, this);
37260             
37261             Roo.each(standard, function(s){
37262                 
37263                 if(String(s) != String(format)){
37264                     return;
37265                 }
37266                 
37267                 match = true;
37268                 return false;
37269                 
37270             }, this);
37271             
37272             if(!match && length == 1){
37273                 return;
37274             }
37275             
37276             if(!match){
37277                 filterPattern(box, length - 1);
37278                 return;
37279             }
37280                 
37281             queue.push(pattern);
37282
37283             box = box.slice(length, box.length);
37284
37285             filterPattern(box, 4);
37286
37287             return;
37288             
37289         }
37290         
37291         Roo.each(boxes, function(box, k){
37292             
37293             if(!box.length){
37294                 return;
37295             }
37296             
37297             if(box.length == 1){
37298                 queue.push(box);
37299                 return;
37300             }
37301             
37302             filterPattern(box, 4);
37303             
37304         }, this);
37305         
37306         this._processVerticalLayoutQueue( queue, isInstant );
37307         
37308     },
37309     
37310 //    _verticalAlternativeLayoutItems : function( items , isInstant )
37311 //    {
37312 //        if ( !items || !items.length ) {
37313 //            return;
37314 //        }
37315 //
37316 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
37317 //        
37318 //    },
37319     
37320     _horizontalLayoutItems : function ( items , isInstant)
37321     {
37322         if ( !items || !items.length || items.length < 3) {
37323             return;
37324         }
37325         
37326         items.reverse();
37327         
37328         var eItems = items.slice(0, 3);
37329         
37330         items = items.slice(3, items.length);
37331         
37332         var standard = [
37333             ['xs', 'xs', 'xs', 'wide'],
37334             ['xs', 'xs', 'wide'],
37335             ['xs', 'xs', 'sm'],
37336             ['xs', 'xs', 'xs'],
37337             ['xs', 'wide'],
37338             ['xs', 'sm'],
37339             ['xs', 'xs'],
37340             ['xs'],
37341             
37342             ['sm', 'xs', 'xs'],
37343             ['sm', 'xs'],
37344             ['sm'],
37345             
37346             ['wide', 'xs', 'xs', 'xs'],
37347             ['wide', 'xs', 'xs'],
37348             ['wide', 'xs'],
37349             ['wide'],
37350             
37351             ['wide-thin']
37352         ];
37353         
37354         var queue = [];
37355         
37356         var boxes = [];
37357         
37358         var box = [];
37359         
37360         Roo.each(items, function(item, k){
37361             
37362             switch (item.size) {
37363                 case 'md' :
37364                 case 'md-left' :
37365                 case 'md-right' :
37366                 case 'tall' :
37367                     
37368                     if(box.length){
37369                         boxes.push(box);
37370                         box = [];
37371                     }
37372                     
37373                     boxes.push([item]);
37374                     
37375                     break;
37376                     
37377                 case 'xs' :
37378                 case 'sm' :
37379                 case 'wide' :
37380                 case 'wide-thin' :
37381                     
37382                     box.push(item);
37383                     
37384                     break;
37385                 default :
37386                     break;
37387                     
37388             }
37389             
37390         }, this);
37391         
37392         if(box.length){
37393             boxes.push(box);
37394             box = [];
37395         }
37396         
37397         var filterPattern = function(box, length)
37398         {
37399             if(!box.length){
37400                 return;
37401             }
37402             
37403             var match = false;
37404             
37405             var pattern = box.slice(0, length);
37406             
37407             var format = [];
37408             
37409             Roo.each(pattern, function(i){
37410                 format.push(i.size);
37411             }, this);
37412             
37413             Roo.each(standard, function(s){
37414                 
37415                 if(String(s) != String(format)){
37416                     return;
37417                 }
37418                 
37419                 match = true;
37420                 return false;
37421                 
37422             }, this);
37423             
37424             if(!match && length == 1){
37425                 return;
37426             }
37427             
37428             if(!match){
37429                 filterPattern(box, length - 1);
37430                 return;
37431             }
37432                 
37433             queue.push(pattern);
37434
37435             box = box.slice(length, box.length);
37436
37437             filterPattern(box, 4);
37438
37439             return;
37440             
37441         }
37442         
37443         Roo.each(boxes, function(box, k){
37444             
37445             if(!box.length){
37446                 return;
37447             }
37448             
37449             if(box.length == 1){
37450                 queue.push(box);
37451                 return;
37452             }
37453             
37454             filterPattern(box, 4);
37455             
37456         }, this);
37457         
37458         
37459         var prune = [];
37460         
37461         var pos = this.el.getBox(true);
37462         
37463         var minX = pos.x;
37464         
37465         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37466         
37467         var hit_end = false;
37468         
37469         Roo.each(queue, function(box){
37470             
37471             if(hit_end){
37472                 
37473                 Roo.each(box, function(b){
37474                 
37475                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37476                     b.el.hide();
37477
37478                 }, this);
37479
37480                 return;
37481             }
37482             
37483             var mx = 0;
37484             
37485             Roo.each(box, function(b){
37486                 
37487                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37488                 b.el.show();
37489
37490                 mx = Math.max(mx, b.x);
37491                 
37492             }, this);
37493             
37494             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
37495             
37496             if(maxX < minX){
37497                 
37498                 Roo.each(box, function(b){
37499                 
37500                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37501                     b.el.hide();
37502                     
37503                 }, this);
37504                 
37505                 hit_end = true;
37506                 
37507                 return;
37508             }
37509             
37510             prune.push(box);
37511             
37512         }, this);
37513         
37514         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
37515     },
37516     
37517     /** Sets position of item in DOM
37518     * @param {Element} item
37519     * @param {Number} x - horizontal position
37520     * @param {Number} y - vertical position
37521     * @param {Boolean} isInstant - disables transitions
37522     */
37523     _processVerticalLayoutQueue : function( queue, isInstant )
37524     {
37525         var pos = this.el.getBox(true);
37526         var x = pos.x;
37527         var y = pos.y;
37528         var maxY = [];
37529         
37530         for (var i = 0; i < this.cols; i++){
37531             maxY[i] = pos.y;
37532         }
37533         
37534         Roo.each(queue, function(box, k){
37535             
37536             var col = k % this.cols;
37537             
37538             Roo.each(box, function(b,kk){
37539                 
37540                 b.el.position('absolute');
37541                 
37542                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37543                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37544                 
37545                 if(b.size == 'md-left' || b.size == 'md-right'){
37546                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37547                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37548                 }
37549                 
37550                 b.el.setWidth(width);
37551                 b.el.setHeight(height);
37552                 // iframe?
37553                 b.el.select('iframe',true).setSize(width,height);
37554                 
37555             }, this);
37556             
37557             for (var i = 0; i < this.cols; i++){
37558                 
37559                 if(maxY[i] < maxY[col]){
37560                     col = i;
37561                     continue;
37562                 }
37563                 
37564                 col = Math.min(col, i);
37565                 
37566             }
37567             
37568             x = pos.x + col * (this.colWidth + this.padWidth);
37569             
37570             y = maxY[col];
37571             
37572             var positions = [];
37573             
37574             switch (box.length){
37575                 case 1 :
37576                     positions = this.getVerticalOneBoxColPositions(x, y, box);
37577                     break;
37578                 case 2 :
37579                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
37580                     break;
37581                 case 3 :
37582                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
37583                     break;
37584                 case 4 :
37585                     positions = this.getVerticalFourBoxColPositions(x, y, box);
37586                     break;
37587                 default :
37588                     break;
37589             }
37590             
37591             Roo.each(box, function(b,kk){
37592                 
37593                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37594                 
37595                 var sz = b.el.getSize();
37596                 
37597                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
37598                 
37599             }, this);
37600             
37601         }, this);
37602         
37603         var mY = 0;
37604         
37605         for (var i = 0; i < this.cols; i++){
37606             mY = Math.max(mY, maxY[i]);
37607         }
37608         
37609         this.el.setHeight(mY - pos.y);
37610         
37611     },
37612     
37613 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
37614 //    {
37615 //        var pos = this.el.getBox(true);
37616 //        var x = pos.x;
37617 //        var y = pos.y;
37618 //        var maxX = pos.right;
37619 //        
37620 //        var maxHeight = 0;
37621 //        
37622 //        Roo.each(items, function(item, k){
37623 //            
37624 //            var c = k % 2;
37625 //            
37626 //            item.el.position('absolute');
37627 //                
37628 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
37629 //
37630 //            item.el.setWidth(width);
37631 //
37632 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
37633 //
37634 //            item.el.setHeight(height);
37635 //            
37636 //            if(c == 0){
37637 //                item.el.setXY([x, y], isInstant ? false : true);
37638 //            } else {
37639 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
37640 //            }
37641 //            
37642 //            y = y + height + this.alternativePadWidth;
37643 //            
37644 //            maxHeight = maxHeight + height + this.alternativePadWidth;
37645 //            
37646 //        }, this);
37647 //        
37648 //        this.el.setHeight(maxHeight);
37649 //        
37650 //    },
37651     
37652     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
37653     {
37654         var pos = this.el.getBox(true);
37655         
37656         var minX = pos.x;
37657         var minY = pos.y;
37658         
37659         var maxX = pos.right;
37660         
37661         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
37662         
37663         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37664         
37665         Roo.each(queue, function(box, k){
37666             
37667             Roo.each(box, function(b, kk){
37668                 
37669                 b.el.position('absolute');
37670                 
37671                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37672                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37673                 
37674                 if(b.size == 'md-left' || b.size == 'md-right'){
37675                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37676                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37677                 }
37678                 
37679                 b.el.setWidth(width);
37680                 b.el.setHeight(height);
37681                 
37682             }, this);
37683             
37684             if(!box.length){
37685                 return;
37686             }
37687             
37688             var positions = [];
37689             
37690             switch (box.length){
37691                 case 1 :
37692                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
37693                     break;
37694                 case 2 :
37695                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
37696                     break;
37697                 case 3 :
37698                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
37699                     break;
37700                 case 4 :
37701                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
37702                     break;
37703                 default :
37704                     break;
37705             }
37706             
37707             Roo.each(box, function(b,kk){
37708                 
37709                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37710                 
37711                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
37712                 
37713             }, this);
37714             
37715         }, this);
37716         
37717     },
37718     
37719     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
37720     {
37721         Roo.each(eItems, function(b,k){
37722             
37723             b.size = (k == 0) ? 'sm' : 'xs';
37724             b.x = (k == 0) ? 2 : 1;
37725             b.y = (k == 0) ? 2 : 1;
37726             
37727             b.el.position('absolute');
37728             
37729             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37730                 
37731             b.el.setWidth(width);
37732             
37733             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37734             
37735             b.el.setHeight(height);
37736             
37737         }, this);
37738
37739         var positions = [];
37740         
37741         positions.push({
37742             x : maxX - this.unitWidth * 2 - this.gutter,
37743             y : minY
37744         });
37745         
37746         positions.push({
37747             x : maxX - this.unitWidth,
37748             y : minY + (this.unitWidth + this.gutter) * 2
37749         });
37750         
37751         positions.push({
37752             x : maxX - this.unitWidth * 3 - this.gutter * 2,
37753             y : minY
37754         });
37755         
37756         Roo.each(eItems, function(b,k){
37757             
37758             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
37759
37760         }, this);
37761         
37762     },
37763     
37764     getVerticalOneBoxColPositions : function(x, y, box)
37765     {
37766         var pos = [];
37767         
37768         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
37769         
37770         if(box[0].size == 'md-left'){
37771             rand = 0;
37772         }
37773         
37774         if(box[0].size == 'md-right'){
37775             rand = 1;
37776         }
37777         
37778         pos.push({
37779             x : x + (this.unitWidth + this.gutter) * rand,
37780             y : y
37781         });
37782         
37783         return pos;
37784     },
37785     
37786     getVerticalTwoBoxColPositions : function(x, y, box)
37787     {
37788         var pos = [];
37789         
37790         if(box[0].size == 'xs'){
37791             
37792             pos.push({
37793                 x : x,
37794                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
37795             });
37796
37797             pos.push({
37798                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
37799                 y : y
37800             });
37801             
37802             return pos;
37803             
37804         }
37805         
37806         pos.push({
37807             x : x,
37808             y : y
37809         });
37810
37811         pos.push({
37812             x : x + (this.unitWidth + this.gutter) * 2,
37813             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
37814         });
37815         
37816         return pos;
37817         
37818     },
37819     
37820     getVerticalThreeBoxColPositions : function(x, y, box)
37821     {
37822         var pos = [];
37823         
37824         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37825             
37826             pos.push({
37827                 x : x,
37828                 y : y
37829             });
37830
37831             pos.push({
37832                 x : x + (this.unitWidth + this.gutter) * 1,
37833                 y : y
37834             });
37835             
37836             pos.push({
37837                 x : x + (this.unitWidth + this.gutter) * 2,
37838                 y : y
37839             });
37840             
37841             return pos;
37842             
37843         }
37844         
37845         if(box[0].size == 'xs' && box[1].size == 'xs'){
37846             
37847             pos.push({
37848                 x : x,
37849                 y : y
37850             });
37851
37852             pos.push({
37853                 x : x,
37854                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
37855             });
37856             
37857             pos.push({
37858                 x : x + (this.unitWidth + this.gutter) * 1,
37859                 y : y
37860             });
37861             
37862             return pos;
37863             
37864         }
37865         
37866         pos.push({
37867             x : x,
37868             y : y
37869         });
37870
37871         pos.push({
37872             x : x + (this.unitWidth + this.gutter) * 2,
37873             y : y
37874         });
37875
37876         pos.push({
37877             x : x + (this.unitWidth + this.gutter) * 2,
37878             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
37879         });
37880             
37881         return pos;
37882         
37883     },
37884     
37885     getVerticalFourBoxColPositions : function(x, y, box)
37886     {
37887         var pos = [];
37888         
37889         if(box[0].size == 'xs'){
37890             
37891             pos.push({
37892                 x : x,
37893                 y : y
37894             });
37895
37896             pos.push({
37897                 x : x,
37898                 y : y + (this.unitHeight + this.gutter) * 1
37899             });
37900             
37901             pos.push({
37902                 x : x,
37903                 y : y + (this.unitHeight + this.gutter) * 2
37904             });
37905             
37906             pos.push({
37907                 x : x + (this.unitWidth + this.gutter) * 1,
37908                 y : y
37909             });
37910             
37911             return pos;
37912             
37913         }
37914         
37915         pos.push({
37916             x : x,
37917             y : y
37918         });
37919
37920         pos.push({
37921             x : x + (this.unitWidth + this.gutter) * 2,
37922             y : y
37923         });
37924
37925         pos.push({
37926             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
37927             y : y + (this.unitHeight + this.gutter) * 1
37928         });
37929
37930         pos.push({
37931             x : x + (this.unitWidth + this.gutter) * 2,
37932             y : y + (this.unitWidth + this.gutter) * 2
37933         });
37934
37935         return pos;
37936         
37937     },
37938     
37939     getHorizontalOneBoxColPositions : function(maxX, minY, box)
37940     {
37941         var pos = [];
37942         
37943         if(box[0].size == 'md-left'){
37944             pos.push({
37945                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37946                 y : minY
37947             });
37948             
37949             return pos;
37950         }
37951         
37952         if(box[0].size == 'md-right'){
37953             pos.push({
37954                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37955                 y : minY + (this.unitWidth + this.gutter) * 1
37956             });
37957             
37958             return pos;
37959         }
37960         
37961         var rand = Math.floor(Math.random() * (4 - box[0].y));
37962         
37963         pos.push({
37964             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37965             y : minY + (this.unitWidth + this.gutter) * rand
37966         });
37967         
37968         return pos;
37969         
37970     },
37971     
37972     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
37973     {
37974         var pos = [];
37975         
37976         if(box[0].size == 'xs'){
37977             
37978             pos.push({
37979                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37980                 y : minY
37981             });
37982
37983             pos.push({
37984                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37985                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
37986             });
37987             
37988             return pos;
37989             
37990         }
37991         
37992         pos.push({
37993             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37994             y : minY
37995         });
37996
37997         pos.push({
37998             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37999             y : minY + (this.unitWidth + this.gutter) * 2
38000         });
38001         
38002         return pos;
38003         
38004     },
38005     
38006     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
38007     {
38008         var pos = [];
38009         
38010         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
38011             
38012             pos.push({
38013                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38014                 y : minY
38015             });
38016
38017             pos.push({
38018                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38019                 y : minY + (this.unitWidth + this.gutter) * 1
38020             });
38021             
38022             pos.push({
38023                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38024                 y : minY + (this.unitWidth + this.gutter) * 2
38025             });
38026             
38027             return pos;
38028             
38029         }
38030         
38031         if(box[0].size == 'xs' && box[1].size == 'xs'){
38032             
38033             pos.push({
38034                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38035                 y : minY
38036             });
38037
38038             pos.push({
38039                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38040                 y : minY
38041             });
38042             
38043             pos.push({
38044                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38045                 y : minY + (this.unitWidth + this.gutter) * 1
38046             });
38047             
38048             return pos;
38049             
38050         }
38051         
38052         pos.push({
38053             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38054             y : minY
38055         });
38056
38057         pos.push({
38058             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38059             y : minY + (this.unitWidth + this.gutter) * 2
38060         });
38061
38062         pos.push({
38063             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38064             y : minY + (this.unitWidth + this.gutter) * 2
38065         });
38066             
38067         return pos;
38068         
38069     },
38070     
38071     getHorizontalFourBoxColPositions : function(maxX, minY, box)
38072     {
38073         var pos = [];
38074         
38075         if(box[0].size == 'xs'){
38076             
38077             pos.push({
38078                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38079                 y : minY
38080             });
38081
38082             pos.push({
38083                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38084                 y : minY
38085             });
38086             
38087             pos.push({
38088                 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),
38089                 y : minY
38090             });
38091             
38092             pos.push({
38093                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
38094                 y : minY + (this.unitWidth + this.gutter) * 1
38095             });
38096             
38097             return pos;
38098             
38099         }
38100         
38101         pos.push({
38102             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38103             y : minY
38104         });
38105         
38106         pos.push({
38107             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38108             y : minY + (this.unitWidth + this.gutter) * 2
38109         });
38110         
38111         pos.push({
38112             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38113             y : minY + (this.unitWidth + this.gutter) * 2
38114         });
38115         
38116         pos.push({
38117             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),
38118             y : minY + (this.unitWidth + this.gutter) * 2
38119         });
38120
38121         return pos;
38122         
38123     },
38124     
38125     /**
38126     * remove a Masonry Brick
38127     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
38128     */
38129     removeBrick : function(brick_id)
38130     {
38131         if (!brick_id) {
38132             return;
38133         }
38134         
38135         for (var i = 0; i<this.bricks.length; i++) {
38136             if (this.bricks[i].id == brick_id) {
38137                 this.bricks.splice(i,1);
38138                 this.el.dom.removeChild(Roo.get(brick_id).dom);
38139                 this.initial();
38140             }
38141         }
38142     },
38143     
38144     /**
38145     * adds a Masonry Brick
38146     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
38147     */
38148     addBrick : function(cfg)
38149     {
38150         var cn = new Roo.bootstrap.MasonryBrick(cfg);
38151         //this.register(cn);
38152         cn.parentId = this.id;
38153         cn.render(this.el);
38154         return cn;
38155     },
38156     
38157     /**
38158     * register a Masonry Brick
38159     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
38160     */
38161     
38162     register : function(brick)
38163     {
38164         this.bricks.push(brick);
38165         brick.masonryId = this.id;
38166     },
38167     
38168     /**
38169     * clear all the Masonry Brick
38170     */
38171     clearAll : function()
38172     {
38173         this.bricks = [];
38174         //this.getChildContainer().dom.innerHTML = "";
38175         this.el.dom.innerHTML = '';
38176     },
38177     
38178     getSelected : function()
38179     {
38180         if (!this.selectedBrick) {
38181             return false;
38182         }
38183         
38184         return this.selectedBrick;
38185     }
38186 });
38187
38188 Roo.apply(Roo.bootstrap.LayoutMasonry, {
38189     
38190     groups: {},
38191      /**
38192     * register a Masonry Layout
38193     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
38194     */
38195     
38196     register : function(layout)
38197     {
38198         this.groups[layout.id] = layout;
38199     },
38200     /**
38201     * fetch a  Masonry Layout based on the masonry layout ID
38202     * @param {string} the masonry layout to add
38203     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
38204     */
38205     
38206     get: function(layout_id) {
38207         if (typeof(this.groups[layout_id]) == 'undefined') {
38208             return false;
38209         }
38210         return this.groups[layout_id] ;
38211     }
38212     
38213     
38214     
38215 });
38216
38217  
38218
38219  /**
38220  *
38221  * This is based on 
38222  * http://masonry.desandro.com
38223  *
38224  * The idea is to render all the bricks based on vertical width...
38225  *
38226  * The original code extends 'outlayer' - we might need to use that....
38227  * 
38228  */
38229
38230
38231 /**
38232  * @class Roo.bootstrap.LayoutMasonryAuto
38233  * @extends Roo.bootstrap.Component
38234  * Bootstrap Layout Masonry class
38235  * 
38236  * @constructor
38237  * Create a new Element
38238  * @param {Object} config The config object
38239  */
38240
38241 Roo.bootstrap.LayoutMasonryAuto = function(config){
38242     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
38243 };
38244
38245 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
38246     
38247       /**
38248      * @cfg {Boolean} isFitWidth  - resize the width..
38249      */   
38250     isFitWidth : false,  // options..
38251     /**
38252      * @cfg {Boolean} isOriginLeft = left align?
38253      */   
38254     isOriginLeft : true,
38255     /**
38256      * @cfg {Boolean} isOriginTop = top align?
38257      */   
38258     isOriginTop : false,
38259     /**
38260      * @cfg {Boolean} isLayoutInstant = no animation?
38261      */   
38262     isLayoutInstant : false, // needed?
38263     /**
38264      * @cfg {Boolean} isResizingContainer = not sure if this is used..
38265      */   
38266     isResizingContainer : true,
38267     /**
38268      * @cfg {Number} columnWidth  width of the columns 
38269      */   
38270     
38271     columnWidth : 0,
38272     
38273     /**
38274      * @cfg {Number} maxCols maximum number of columns
38275      */   
38276     
38277     maxCols: 0,
38278     /**
38279      * @cfg {Number} padHeight padding below box..
38280      */   
38281     
38282     padHeight : 10, 
38283     
38284     /**
38285      * @cfg {Boolean} isAutoInitial defalut true
38286      */   
38287     
38288     isAutoInitial : true, 
38289     
38290     // private?
38291     gutter : 0,
38292     
38293     containerWidth: 0,
38294     initialColumnWidth : 0,
38295     currentSize : null,
38296     
38297     colYs : null, // array.
38298     maxY : 0,
38299     padWidth: 10,
38300     
38301     
38302     tag: 'div',
38303     cls: '',
38304     bricks: null, //CompositeElement
38305     cols : 0, // array?
38306     // element : null, // wrapped now this.el
38307     _isLayoutInited : null, 
38308     
38309     
38310     getAutoCreate : function(){
38311         
38312         var cfg = {
38313             tag: this.tag,
38314             cls: 'blog-masonary-wrapper ' + this.cls,
38315             cn : {
38316                 cls : 'mas-boxes masonary'
38317             }
38318         };
38319         
38320         return cfg;
38321     },
38322     
38323     getChildContainer: function( )
38324     {
38325         if (this.boxesEl) {
38326             return this.boxesEl;
38327         }
38328         
38329         this.boxesEl = this.el.select('.mas-boxes').first();
38330         
38331         return this.boxesEl;
38332     },
38333     
38334     
38335     initEvents : function()
38336     {
38337         var _this = this;
38338         
38339         if(this.isAutoInitial){
38340             Roo.log('hook children rendered');
38341             this.on('childrenrendered', function() {
38342                 Roo.log('children rendered');
38343                 _this.initial();
38344             } ,this);
38345         }
38346         
38347     },
38348     
38349     initial : function()
38350     {
38351         this.reloadItems();
38352
38353         this.currentSize = this.el.getBox(true);
38354
38355         /// was window resize... - let's see if this works..
38356         Roo.EventManager.onWindowResize(this.resize, this); 
38357
38358         if(!this.isAutoInitial){
38359             this.layout();
38360             return;
38361         }
38362         
38363         this.layout.defer(500,this);
38364     },
38365     
38366     reloadItems: function()
38367     {
38368         this.bricks = this.el.select('.masonry-brick', true);
38369         
38370         this.bricks.each(function(b) {
38371             //Roo.log(b.getSize());
38372             if (!b.attr('originalwidth')) {
38373                 b.attr('originalwidth',  b.getSize().width);
38374             }
38375             
38376         });
38377         
38378         Roo.log(this.bricks.elements.length);
38379     },
38380     
38381     resize : function()
38382     {
38383         Roo.log('resize');
38384         var cs = this.el.getBox(true);
38385         
38386         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
38387             Roo.log("no change in with or X");
38388             return;
38389         }
38390         this.currentSize = cs;
38391         this.layout();
38392     },
38393     
38394     layout : function()
38395     {
38396          Roo.log('layout');
38397         this._resetLayout();
38398         //this._manageStamps();
38399       
38400         // don't animate first layout
38401         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38402         this.layoutItems( isInstant );
38403       
38404         // flag for initalized
38405         this._isLayoutInited = true;
38406     },
38407     
38408     layoutItems : function( isInstant )
38409     {
38410         //var items = this._getItemsForLayout( this.items );
38411         // original code supports filtering layout items.. we just ignore it..
38412         
38413         this._layoutItems( this.bricks , isInstant );
38414       
38415         this._postLayout();
38416     },
38417     _layoutItems : function ( items , isInstant)
38418     {
38419        //this.fireEvent( 'layout', this, items );
38420     
38421
38422         if ( !items || !items.elements.length ) {
38423           // no items, emit event with empty array
38424             return;
38425         }
38426
38427         var queue = [];
38428         items.each(function(item) {
38429             Roo.log("layout item");
38430             Roo.log(item);
38431             // get x/y object from method
38432             var position = this._getItemLayoutPosition( item );
38433             // enqueue
38434             position.item = item;
38435             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
38436             queue.push( position );
38437         }, this);
38438       
38439         this._processLayoutQueue( queue );
38440     },
38441     /** Sets position of item in DOM
38442     * @param {Element} item
38443     * @param {Number} x - horizontal position
38444     * @param {Number} y - vertical position
38445     * @param {Boolean} isInstant - disables transitions
38446     */
38447     _processLayoutQueue : function( queue )
38448     {
38449         for ( var i=0, len = queue.length; i < len; i++ ) {
38450             var obj = queue[i];
38451             obj.item.position('absolute');
38452             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
38453         }
38454     },
38455       
38456     
38457     /**
38458     * Any logic you want to do after each layout,
38459     * i.e. size the container
38460     */
38461     _postLayout : function()
38462     {
38463         this.resizeContainer();
38464     },
38465     
38466     resizeContainer : function()
38467     {
38468         if ( !this.isResizingContainer ) {
38469             return;
38470         }
38471         var size = this._getContainerSize();
38472         if ( size ) {
38473             this.el.setSize(size.width,size.height);
38474             this.boxesEl.setSize(size.width,size.height);
38475         }
38476     },
38477     
38478     
38479     
38480     _resetLayout : function()
38481     {
38482         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
38483         this.colWidth = this.el.getWidth();
38484         //this.gutter = this.el.getWidth(); 
38485         
38486         this.measureColumns();
38487
38488         // reset column Y
38489         var i = this.cols;
38490         this.colYs = [];
38491         while (i--) {
38492             this.colYs.push( 0 );
38493         }
38494     
38495         this.maxY = 0;
38496     },
38497
38498     measureColumns : function()
38499     {
38500         this.getContainerWidth();
38501       // if columnWidth is 0, default to outerWidth of first item
38502         if ( !this.columnWidth ) {
38503             var firstItem = this.bricks.first();
38504             Roo.log(firstItem);
38505             this.columnWidth  = this.containerWidth;
38506             if (firstItem && firstItem.attr('originalwidth') ) {
38507                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
38508             }
38509             // columnWidth fall back to item of first element
38510             Roo.log("set column width?");
38511                         this.initialColumnWidth = this.columnWidth  ;
38512
38513             // if first elem has no width, default to size of container
38514             
38515         }
38516         
38517         
38518         if (this.initialColumnWidth) {
38519             this.columnWidth = this.initialColumnWidth;
38520         }
38521         
38522         
38523             
38524         // column width is fixed at the top - however if container width get's smaller we should
38525         // reduce it...
38526         
38527         // this bit calcs how man columns..
38528             
38529         var columnWidth = this.columnWidth += this.gutter;
38530       
38531         // calculate columns
38532         var containerWidth = this.containerWidth + this.gutter;
38533         
38534         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
38535         // fix rounding errors, typically with gutters
38536         var excess = columnWidth - containerWidth % columnWidth;
38537         
38538         
38539         // if overshoot is less than a pixel, round up, otherwise floor it
38540         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
38541         cols = Math[ mathMethod ]( cols );
38542         this.cols = Math.max( cols, 1 );
38543         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38544         
38545          // padding positioning..
38546         var totalColWidth = this.cols * this.columnWidth;
38547         var padavail = this.containerWidth - totalColWidth;
38548         // so for 2 columns - we need 3 'pads'
38549         
38550         var padNeeded = (1+this.cols) * this.padWidth;
38551         
38552         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
38553         
38554         this.columnWidth += padExtra
38555         //this.padWidth = Math.floor(padavail /  ( this.cols));
38556         
38557         // adjust colum width so that padding is fixed??
38558         
38559         // we have 3 columns ... total = width * 3
38560         // we have X left over... that should be used by 
38561         
38562         //if (this.expandC) {
38563             
38564         //}
38565         
38566         
38567         
38568     },
38569     
38570     getContainerWidth : function()
38571     {
38572        /* // container is parent if fit width
38573         var container = this.isFitWidth ? this.element.parentNode : this.element;
38574         // check that this.size and size are there
38575         // IE8 triggers resize on body size change, so they might not be
38576         
38577         var size = getSize( container );  //FIXME
38578         this.containerWidth = size && size.innerWidth; //FIXME
38579         */
38580          
38581         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38582         
38583     },
38584     
38585     _getItemLayoutPosition : function( item )  // what is item?
38586     {
38587         // we resize the item to our columnWidth..
38588       
38589         item.setWidth(this.columnWidth);
38590         item.autoBoxAdjust  = false;
38591         
38592         var sz = item.getSize();
38593  
38594         // how many columns does this brick span
38595         var remainder = this.containerWidth % this.columnWidth;
38596         
38597         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
38598         // round if off by 1 pixel, otherwise use ceil
38599         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
38600         colSpan = Math.min( colSpan, this.cols );
38601         
38602         // normally this should be '1' as we dont' currently allow multi width columns..
38603         
38604         var colGroup = this._getColGroup( colSpan );
38605         // get the minimum Y value from the columns
38606         var minimumY = Math.min.apply( Math, colGroup );
38607         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38608         
38609         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
38610          
38611         // position the brick
38612         var position = {
38613             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
38614             y: this.currentSize.y + minimumY + this.padHeight
38615         };
38616         
38617         Roo.log(position);
38618         // apply setHeight to necessary columns
38619         var setHeight = minimumY + sz.height + this.padHeight;
38620         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38621         
38622         var setSpan = this.cols + 1 - colGroup.length;
38623         for ( var i = 0; i < setSpan; i++ ) {
38624           this.colYs[ shortColIndex + i ] = setHeight ;
38625         }
38626       
38627         return position;
38628     },
38629     
38630     /**
38631      * @param {Number} colSpan - number of columns the element spans
38632      * @returns {Array} colGroup
38633      */
38634     _getColGroup : function( colSpan )
38635     {
38636         if ( colSpan < 2 ) {
38637           // if brick spans only one column, use all the column Ys
38638           return this.colYs;
38639         }
38640       
38641         var colGroup = [];
38642         // how many different places could this brick fit horizontally
38643         var groupCount = this.cols + 1 - colSpan;
38644         // for each group potential horizontal position
38645         for ( var i = 0; i < groupCount; i++ ) {
38646           // make an array of colY values for that one group
38647           var groupColYs = this.colYs.slice( i, i + colSpan );
38648           // and get the max value of the array
38649           colGroup[i] = Math.max.apply( Math, groupColYs );
38650         }
38651         return colGroup;
38652     },
38653     /*
38654     _manageStamp : function( stamp )
38655     {
38656         var stampSize =  stamp.getSize();
38657         var offset = stamp.getBox();
38658         // get the columns that this stamp affects
38659         var firstX = this.isOriginLeft ? offset.x : offset.right;
38660         var lastX = firstX + stampSize.width;
38661         var firstCol = Math.floor( firstX / this.columnWidth );
38662         firstCol = Math.max( 0, firstCol );
38663         
38664         var lastCol = Math.floor( lastX / this.columnWidth );
38665         // lastCol should not go over if multiple of columnWidth #425
38666         lastCol -= lastX % this.columnWidth ? 0 : 1;
38667         lastCol = Math.min( this.cols - 1, lastCol );
38668         
38669         // set colYs to bottom of the stamp
38670         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
38671             stampSize.height;
38672             
38673         for ( var i = firstCol; i <= lastCol; i++ ) {
38674           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
38675         }
38676     },
38677     */
38678     
38679     _getContainerSize : function()
38680     {
38681         this.maxY = Math.max.apply( Math, this.colYs );
38682         var size = {
38683             height: this.maxY
38684         };
38685       
38686         if ( this.isFitWidth ) {
38687             size.width = this._getContainerFitWidth();
38688         }
38689       
38690         return size;
38691     },
38692     
38693     _getContainerFitWidth : function()
38694     {
38695         var unusedCols = 0;
38696         // count unused columns
38697         var i = this.cols;
38698         while ( --i ) {
38699           if ( this.colYs[i] !== 0 ) {
38700             break;
38701           }
38702           unusedCols++;
38703         }
38704         // fit container to columns that have been used
38705         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
38706     },
38707     
38708     needsResizeLayout : function()
38709     {
38710         var previousWidth = this.containerWidth;
38711         this.getContainerWidth();
38712         return previousWidth !== this.containerWidth;
38713     }
38714  
38715 });
38716
38717  
38718
38719  /*
38720  * - LGPL
38721  *
38722  * element
38723  * 
38724  */
38725
38726 /**
38727  * @class Roo.bootstrap.MasonryBrick
38728  * @extends Roo.bootstrap.Component
38729  * Bootstrap MasonryBrick class
38730  * 
38731  * @constructor
38732  * Create a new MasonryBrick
38733  * @param {Object} config The config object
38734  */
38735
38736 Roo.bootstrap.MasonryBrick = function(config){
38737     
38738     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
38739     
38740     Roo.bootstrap.MasonryBrick.register(this);
38741     
38742     this.addEvents({
38743         // raw events
38744         /**
38745          * @event click
38746          * When a MasonryBrick is clcik
38747          * @param {Roo.bootstrap.MasonryBrick} this
38748          * @param {Roo.EventObject} e
38749          */
38750         "click" : true
38751     });
38752 };
38753
38754 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
38755     
38756     /**
38757      * @cfg {String} title
38758      */   
38759     title : '',
38760     /**
38761      * @cfg {String} html
38762      */   
38763     html : '',
38764     /**
38765      * @cfg {String} bgimage
38766      */   
38767     bgimage : '',
38768     /**
38769      * @cfg {String} videourl
38770      */   
38771     videourl : '',
38772     /**
38773      * @cfg {String} cls
38774      */   
38775     cls : '',
38776     /**
38777      * @cfg {String} href
38778      */   
38779     href : '',
38780     /**
38781      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
38782      */   
38783     size : 'xs',
38784     
38785     /**
38786      * @cfg {String} placetitle (center|bottom)
38787      */   
38788     placetitle : '',
38789     
38790     /**
38791      * @cfg {Boolean} isFitContainer defalut true
38792      */   
38793     isFitContainer : true, 
38794     
38795     /**
38796      * @cfg {Boolean} preventDefault defalut false
38797      */   
38798     preventDefault : false, 
38799     
38800     /**
38801      * @cfg {Boolean} inverse defalut false
38802      */   
38803     maskInverse : false, 
38804     
38805     getAutoCreate : function()
38806     {
38807         if(!this.isFitContainer){
38808             return this.getSplitAutoCreate();
38809         }
38810         
38811         var cls = 'masonry-brick masonry-brick-full';
38812         
38813         if(this.href.length){
38814             cls += ' masonry-brick-link';
38815         }
38816         
38817         if(this.bgimage.length){
38818             cls += ' masonry-brick-image';
38819         }
38820         
38821         if(this.maskInverse){
38822             cls += ' mask-inverse';
38823         }
38824         
38825         if(!this.html.length && !this.maskInverse && !this.videourl.length){
38826             cls += ' enable-mask';
38827         }
38828         
38829         if(this.size){
38830             cls += ' masonry-' + this.size + '-brick';
38831         }
38832         
38833         if(this.placetitle.length){
38834             
38835             switch (this.placetitle) {
38836                 case 'center' :
38837                     cls += ' masonry-center-title';
38838                     break;
38839                 case 'bottom' :
38840                     cls += ' masonry-bottom-title';
38841                     break;
38842                 default:
38843                     break;
38844             }
38845             
38846         } else {
38847             if(!this.html.length && !this.bgimage.length){
38848                 cls += ' masonry-center-title';
38849             }
38850
38851             if(!this.html.length && this.bgimage.length){
38852                 cls += ' masonry-bottom-title';
38853             }
38854         }
38855         
38856         if(this.cls){
38857             cls += ' ' + this.cls;
38858         }
38859         
38860         var cfg = {
38861             tag: (this.href.length) ? 'a' : 'div',
38862             cls: cls,
38863             cn: [
38864                 {
38865                     tag: 'div',
38866                     cls: 'masonry-brick-mask'
38867                 },
38868                 {
38869                     tag: 'div',
38870                     cls: 'masonry-brick-paragraph',
38871                     cn: []
38872                 }
38873             ]
38874         };
38875         
38876         if(this.href.length){
38877             cfg.href = this.href;
38878         }
38879         
38880         var cn = cfg.cn[1].cn;
38881         
38882         if(this.title.length){
38883             cn.push({
38884                 tag: 'h4',
38885                 cls: 'masonry-brick-title',
38886                 html: this.title
38887             });
38888         }
38889         
38890         if(this.html.length){
38891             cn.push({
38892                 tag: 'p',
38893                 cls: 'masonry-brick-text',
38894                 html: this.html
38895             });
38896         }
38897         
38898         if (!this.title.length && !this.html.length) {
38899             cfg.cn[1].cls += ' hide';
38900         }
38901         
38902         if(this.bgimage.length){
38903             cfg.cn.push({
38904                 tag: 'img',
38905                 cls: 'masonry-brick-image-view',
38906                 src: this.bgimage
38907             });
38908         }
38909         
38910         if(this.videourl.length){
38911             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38912             // youtube support only?
38913             cfg.cn.push({
38914                 tag: 'iframe',
38915                 cls: 'masonry-brick-image-view',
38916                 src: vurl,
38917                 frameborder : 0,
38918                 allowfullscreen : true
38919             });
38920         }
38921         
38922         return cfg;
38923         
38924     },
38925     
38926     getSplitAutoCreate : function()
38927     {
38928         var cls = 'masonry-brick masonry-brick-split';
38929         
38930         if(this.href.length){
38931             cls += ' masonry-brick-link';
38932         }
38933         
38934         if(this.bgimage.length){
38935             cls += ' masonry-brick-image';
38936         }
38937         
38938         if(this.size){
38939             cls += ' masonry-' + this.size + '-brick';
38940         }
38941         
38942         switch (this.placetitle) {
38943             case 'center' :
38944                 cls += ' masonry-center-title';
38945                 break;
38946             case 'bottom' :
38947                 cls += ' masonry-bottom-title';
38948                 break;
38949             default:
38950                 if(!this.bgimage.length){
38951                     cls += ' masonry-center-title';
38952                 }
38953
38954                 if(this.bgimage.length){
38955                     cls += ' masonry-bottom-title';
38956                 }
38957                 break;
38958         }
38959         
38960         if(this.cls){
38961             cls += ' ' + this.cls;
38962         }
38963         
38964         var cfg = {
38965             tag: (this.href.length) ? 'a' : 'div',
38966             cls: cls,
38967             cn: [
38968                 {
38969                     tag: 'div',
38970                     cls: 'masonry-brick-split-head',
38971                     cn: [
38972                         {
38973                             tag: 'div',
38974                             cls: 'masonry-brick-paragraph',
38975                             cn: []
38976                         }
38977                     ]
38978                 },
38979                 {
38980                     tag: 'div',
38981                     cls: 'masonry-brick-split-body',
38982                     cn: []
38983                 }
38984             ]
38985         };
38986         
38987         if(this.href.length){
38988             cfg.href = this.href;
38989         }
38990         
38991         if(this.title.length){
38992             cfg.cn[0].cn[0].cn.push({
38993                 tag: 'h4',
38994                 cls: 'masonry-brick-title',
38995                 html: this.title
38996             });
38997         }
38998         
38999         if(this.html.length){
39000             cfg.cn[1].cn.push({
39001                 tag: 'p',
39002                 cls: 'masonry-brick-text',
39003                 html: this.html
39004             });
39005         }
39006
39007         if(this.bgimage.length){
39008             cfg.cn[0].cn.push({
39009                 tag: 'img',
39010                 cls: 'masonry-brick-image-view',
39011                 src: this.bgimage
39012             });
39013         }
39014         
39015         if(this.videourl.length){
39016             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
39017             // youtube support only?
39018             cfg.cn[0].cn.cn.push({
39019                 tag: 'iframe',
39020                 cls: 'masonry-brick-image-view',
39021                 src: vurl,
39022                 frameborder : 0,
39023                 allowfullscreen : true
39024             });
39025         }
39026         
39027         return cfg;
39028     },
39029     
39030     initEvents: function() 
39031     {
39032         switch (this.size) {
39033             case 'xs' :
39034                 this.x = 1;
39035                 this.y = 1;
39036                 break;
39037             case 'sm' :
39038                 this.x = 2;
39039                 this.y = 2;
39040                 break;
39041             case 'md' :
39042             case 'md-left' :
39043             case 'md-right' :
39044                 this.x = 3;
39045                 this.y = 3;
39046                 break;
39047             case 'tall' :
39048                 this.x = 2;
39049                 this.y = 3;
39050                 break;
39051             case 'wide' :
39052                 this.x = 3;
39053                 this.y = 2;
39054                 break;
39055             case 'wide-thin' :
39056                 this.x = 3;
39057                 this.y = 1;
39058                 break;
39059                         
39060             default :
39061                 break;
39062         }
39063         
39064         if(Roo.isTouch){
39065             this.el.on('touchstart', this.onTouchStart, this);
39066             this.el.on('touchmove', this.onTouchMove, this);
39067             this.el.on('touchend', this.onTouchEnd, this);
39068             this.el.on('contextmenu', this.onContextMenu, this);
39069         } else {
39070             this.el.on('mouseenter'  ,this.enter, this);
39071             this.el.on('mouseleave', this.leave, this);
39072             this.el.on('click', this.onClick, this);
39073         }
39074         
39075         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
39076             this.parent().bricks.push(this);   
39077         }
39078         
39079     },
39080     
39081     onClick: function(e, el)
39082     {
39083         var time = this.endTimer - this.startTimer;
39084         // Roo.log(e.preventDefault());
39085         if(Roo.isTouch){
39086             if(time > 1000){
39087                 e.preventDefault();
39088                 return;
39089             }
39090         }
39091         
39092         if(!this.preventDefault){
39093             return;
39094         }
39095         
39096         e.preventDefault();
39097         
39098         if (this.activeClass != '') {
39099             this.selectBrick();
39100         }
39101         
39102         this.fireEvent('click', this, e);
39103     },
39104     
39105     enter: function(e, el)
39106     {
39107         e.preventDefault();
39108         
39109         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
39110             return;
39111         }
39112         
39113         if(this.bgimage.length && this.html.length){
39114             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
39115         }
39116     },
39117     
39118     leave: function(e, el)
39119     {
39120         e.preventDefault();
39121         
39122         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
39123             return;
39124         }
39125         
39126         if(this.bgimage.length && this.html.length){
39127             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
39128         }
39129     },
39130     
39131     onTouchStart: function(e, el)
39132     {
39133 //        e.preventDefault();
39134         
39135         this.touchmoved = false;
39136         
39137         if(!this.isFitContainer){
39138             return;
39139         }
39140         
39141         if(!this.bgimage.length || !this.html.length){
39142             return;
39143         }
39144         
39145         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
39146         
39147         this.timer = new Date().getTime();
39148         
39149     },
39150     
39151     onTouchMove: function(e, el)
39152     {
39153         this.touchmoved = true;
39154     },
39155     
39156     onContextMenu : function(e,el)
39157     {
39158         e.preventDefault();
39159         e.stopPropagation();
39160         return false;
39161     },
39162     
39163     onTouchEnd: function(e, el)
39164     {
39165 //        e.preventDefault();
39166         
39167         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
39168         
39169             this.leave(e,el);
39170             
39171             return;
39172         }
39173         
39174         if(!this.bgimage.length || !this.html.length){
39175             
39176             if(this.href.length){
39177                 window.location.href = this.href;
39178             }
39179             
39180             return;
39181         }
39182         
39183         if(!this.isFitContainer){
39184             return;
39185         }
39186         
39187         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
39188         
39189         window.location.href = this.href;
39190     },
39191     
39192     //selection on single brick only
39193     selectBrick : function() {
39194         
39195         if (!this.parentId) {
39196             return;
39197         }
39198         
39199         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
39200         var index = m.selectedBrick.indexOf(this.id);
39201         
39202         if ( index > -1) {
39203             m.selectedBrick.splice(index,1);
39204             this.el.removeClass(this.activeClass);
39205             return;
39206         }
39207         
39208         for(var i = 0; i < m.selectedBrick.length; i++) {
39209             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
39210             b.el.removeClass(b.activeClass);
39211         }
39212         
39213         m.selectedBrick = [];
39214         
39215         m.selectedBrick.push(this.id);
39216         this.el.addClass(this.activeClass);
39217         return;
39218     },
39219     
39220     isSelected : function(){
39221         return this.el.hasClass(this.activeClass);
39222         
39223     }
39224 });
39225
39226 Roo.apply(Roo.bootstrap.MasonryBrick, {
39227     
39228     //groups: {},
39229     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
39230      /**
39231     * register a Masonry Brick
39232     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39233     */
39234     
39235     register : function(brick)
39236     {
39237         //this.groups[brick.id] = brick;
39238         this.groups.add(brick.id, brick);
39239     },
39240     /**
39241     * fetch a  masonry brick based on the masonry brick ID
39242     * @param {string} the masonry brick to add
39243     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
39244     */
39245     
39246     get: function(brick_id) 
39247     {
39248         // if (typeof(this.groups[brick_id]) == 'undefined') {
39249         //     return false;
39250         // }
39251         // return this.groups[brick_id] ;
39252         
39253         if(this.groups.key(brick_id)) {
39254             return this.groups.key(brick_id);
39255         }
39256         
39257         return false;
39258     }
39259     
39260     
39261     
39262 });
39263
39264  /*
39265  * - LGPL
39266  *
39267  * element
39268  * 
39269  */
39270
39271 /**
39272  * @class Roo.bootstrap.Brick
39273  * @extends Roo.bootstrap.Component
39274  * Bootstrap Brick class
39275  * 
39276  * @constructor
39277  * Create a new Brick
39278  * @param {Object} config The config object
39279  */
39280
39281 Roo.bootstrap.Brick = function(config){
39282     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
39283     
39284     this.addEvents({
39285         // raw events
39286         /**
39287          * @event click
39288          * When a Brick is click
39289          * @param {Roo.bootstrap.Brick} this
39290          * @param {Roo.EventObject} e
39291          */
39292         "click" : true
39293     });
39294 };
39295
39296 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
39297     
39298     /**
39299      * @cfg {String} title
39300      */   
39301     title : '',
39302     /**
39303      * @cfg {String} html
39304      */   
39305     html : '',
39306     /**
39307      * @cfg {String} bgimage
39308      */   
39309     bgimage : '',
39310     /**
39311      * @cfg {String} cls
39312      */   
39313     cls : '',
39314     /**
39315      * @cfg {String} href
39316      */   
39317     href : '',
39318     /**
39319      * @cfg {String} video
39320      */   
39321     video : '',
39322     /**
39323      * @cfg {Boolean} square
39324      */   
39325     square : true,
39326     
39327     getAutoCreate : function()
39328     {
39329         var cls = 'roo-brick';
39330         
39331         if(this.href.length){
39332             cls += ' roo-brick-link';
39333         }
39334         
39335         if(this.bgimage.length){
39336             cls += ' roo-brick-image';
39337         }
39338         
39339         if(!this.html.length && !this.bgimage.length){
39340             cls += ' roo-brick-center-title';
39341         }
39342         
39343         if(!this.html.length && this.bgimage.length){
39344             cls += ' roo-brick-bottom-title';
39345         }
39346         
39347         if(this.cls){
39348             cls += ' ' + this.cls;
39349         }
39350         
39351         var cfg = {
39352             tag: (this.href.length) ? 'a' : 'div',
39353             cls: cls,
39354             cn: [
39355                 {
39356                     tag: 'div',
39357                     cls: 'roo-brick-paragraph',
39358                     cn: []
39359                 }
39360             ]
39361         };
39362         
39363         if(this.href.length){
39364             cfg.href = this.href;
39365         }
39366         
39367         var cn = cfg.cn[0].cn;
39368         
39369         if(this.title.length){
39370             cn.push({
39371                 tag: 'h4',
39372                 cls: 'roo-brick-title',
39373                 html: this.title
39374             });
39375         }
39376         
39377         if(this.html.length){
39378             cn.push({
39379                 tag: 'p',
39380                 cls: 'roo-brick-text',
39381                 html: this.html
39382             });
39383         } else {
39384             cn.cls += ' hide';
39385         }
39386         
39387         if(this.bgimage.length){
39388             cfg.cn.push({
39389                 tag: 'img',
39390                 cls: 'roo-brick-image-view',
39391                 src: this.bgimage
39392             });
39393         }
39394         
39395         return cfg;
39396     },
39397     
39398     initEvents: function() 
39399     {
39400         if(this.title.length || this.html.length){
39401             this.el.on('mouseenter'  ,this.enter, this);
39402             this.el.on('mouseleave', this.leave, this);
39403         }
39404         
39405         Roo.EventManager.onWindowResize(this.resize, this); 
39406         
39407         if(this.bgimage.length){
39408             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
39409             this.imageEl.on('load', this.onImageLoad, this);
39410             return;
39411         }
39412         
39413         this.resize();
39414     },
39415     
39416     onImageLoad : function()
39417     {
39418         this.resize();
39419     },
39420     
39421     resize : function()
39422     {
39423         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
39424         
39425         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
39426         
39427         if(this.bgimage.length){
39428             var image = this.el.select('.roo-brick-image-view', true).first();
39429             
39430             image.setWidth(paragraph.getWidth());
39431             
39432             if(this.square){
39433                 image.setHeight(paragraph.getWidth());
39434             }
39435             
39436             this.el.setHeight(image.getHeight());
39437             paragraph.setHeight(image.getHeight());
39438             
39439         }
39440         
39441     },
39442     
39443     enter: function(e, el)
39444     {
39445         e.preventDefault();
39446         
39447         if(this.bgimage.length){
39448             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
39449             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
39450         }
39451     },
39452     
39453     leave: function(e, el)
39454     {
39455         e.preventDefault();
39456         
39457         if(this.bgimage.length){
39458             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
39459             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
39460         }
39461     }
39462     
39463 });
39464
39465  
39466
39467  /*
39468  * - LGPL
39469  *
39470  * Number field 
39471  */
39472
39473 /**
39474  * @class Roo.bootstrap.form.NumberField
39475  * @extends Roo.bootstrap.form.Input
39476  * Bootstrap NumberField class
39477  * 
39478  * 
39479  * 
39480  * 
39481  * @constructor
39482  * Create a new NumberField
39483  * @param {Object} config The config object
39484  */
39485
39486 Roo.bootstrap.form.NumberField = function(config){
39487     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
39488 };
39489
39490 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
39491     
39492     /**
39493      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39494      */
39495     allowDecimals : true,
39496     /**
39497      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39498      */
39499     decimalSeparator : ".",
39500     /**
39501      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39502      */
39503     decimalPrecision : 2,
39504     /**
39505      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39506      */
39507     allowNegative : true,
39508     
39509     /**
39510      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
39511      */
39512     allowZero: true,
39513     /**
39514      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39515      */
39516     minValue : Number.NEGATIVE_INFINITY,
39517     /**
39518      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39519      */
39520     maxValue : Number.MAX_VALUE,
39521     /**
39522      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39523      */
39524     minText : "The minimum value for this field is {0}",
39525     /**
39526      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39527      */
39528     maxText : "The maximum value for this field is {0}",
39529     /**
39530      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39531      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39532      */
39533     nanText : "{0} is not a valid number",
39534     /**
39535      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
39536      */
39537     thousandsDelimiter : false,
39538     /**
39539      * @cfg {String} valueAlign alignment of value
39540      */
39541     valueAlign : "left",
39542
39543     getAutoCreate : function()
39544     {
39545         var hiddenInput = {
39546             tag: 'input',
39547             type: 'hidden',
39548             id: Roo.id(),
39549             cls: 'hidden-number-input'
39550         };
39551         
39552         if (this.name) {
39553             hiddenInput.name = this.name;
39554         }
39555         
39556         this.name = '';
39557         
39558         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
39559         
39560         this.name = hiddenInput.name;
39561         
39562         if(cfg.cn.length > 0) {
39563             cfg.cn.push(hiddenInput);
39564         }
39565         
39566         return cfg;
39567     },
39568
39569     // private
39570     initEvents : function()
39571     {   
39572         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
39573         
39574         var allowed = "0123456789";
39575         
39576         if(this.allowDecimals){
39577             allowed += this.decimalSeparator;
39578         }
39579         
39580         if(this.allowNegative){
39581             allowed += "-";
39582         }
39583         
39584         if(this.thousandsDelimiter) {
39585             allowed += ",";
39586         }
39587         
39588         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39589         
39590         var keyPress = function(e){
39591             
39592             var k = e.getKey();
39593             
39594             var c = e.getCharCode();
39595             
39596             if(
39597                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39598                     allowed.indexOf(String.fromCharCode(c)) === -1
39599             ){
39600                 e.stopEvent();
39601                 return;
39602             }
39603             
39604             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39605                 return;
39606             }
39607             
39608             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39609                 e.stopEvent();
39610             }
39611         };
39612         
39613         this.el.on("keypress", keyPress, this);
39614     },
39615     
39616     validateValue : function(value)
39617     {
39618         
39619         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
39620             return false;
39621         }
39622         
39623         var num = this.parseValue(value);
39624         
39625         if(isNaN(num)){
39626             this.markInvalid(String.format(this.nanText, value));
39627             return false;
39628         }
39629         
39630         if(num < this.minValue){
39631             this.markInvalid(String.format(this.minText, this.minValue));
39632             return false;
39633         }
39634         
39635         if(num > this.maxValue){
39636             this.markInvalid(String.format(this.maxText, this.maxValue));
39637             return false;
39638         }
39639         
39640         return true;
39641     },
39642
39643     getValue : function()
39644     {
39645         var v = this.hiddenEl().getValue();
39646         
39647         return this.fixPrecision(this.parseValue(v));
39648     },
39649
39650     parseValue : function(value)
39651     {
39652         if(this.thousandsDelimiter) {
39653             value += "";
39654             r = new RegExp(",", "g");
39655             value = value.replace(r, "");
39656         }
39657         
39658         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39659         return isNaN(value) ? '' : value;
39660     },
39661
39662     fixPrecision : function(value)
39663     {
39664         if(this.thousandsDelimiter) {
39665             value += "";
39666             r = new RegExp(",", "g");
39667             value = value.replace(r, "");
39668         }
39669         
39670         var nan = isNaN(value);
39671         
39672         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
39673             return nan ? '' : value;
39674         }
39675         return parseFloat(value).toFixed(this.decimalPrecision);
39676     },
39677
39678     setValue : function(v)
39679     {
39680         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
39681         
39682         this.value = v;
39683         
39684         if(this.rendered){
39685             
39686             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39687             
39688             this.inputEl().dom.value = (v == '') ? '' :
39689                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
39690             
39691             if(!this.allowZero && v === '0') {
39692                 this.hiddenEl().dom.value = '';
39693                 this.inputEl().dom.value = '';
39694             }
39695             
39696             this.validate();
39697         }
39698     },
39699
39700     decimalPrecisionFcn : function(v)
39701     {
39702         return Math.floor(v);
39703     },
39704
39705     beforeBlur : function()
39706     {
39707         var v = this.parseValue(this.getRawValue());
39708         
39709         if(v || v === 0 || v === ''){
39710             this.setValue(v);
39711         }
39712     },
39713     
39714     hiddenEl : function()
39715     {
39716         return this.el.select('input.hidden-number-input',true).first();
39717     }
39718     
39719 });
39720
39721  
39722
39723 /*
39724 * Licence: LGPL
39725 */
39726
39727 /**
39728  * @class Roo.bootstrap.DocumentSlider
39729  * @extends Roo.bootstrap.Component
39730  * Bootstrap DocumentSlider class
39731  * 
39732  * @constructor
39733  * Create a new DocumentViewer
39734  * @param {Object} config The config object
39735  */
39736
39737 Roo.bootstrap.DocumentSlider = function(config){
39738     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
39739     
39740     this.files = [];
39741     
39742     this.addEvents({
39743         /**
39744          * @event initial
39745          * Fire after initEvent
39746          * @param {Roo.bootstrap.DocumentSlider} this
39747          */
39748         "initial" : true,
39749         /**
39750          * @event update
39751          * Fire after update
39752          * @param {Roo.bootstrap.DocumentSlider} this
39753          */
39754         "update" : true,
39755         /**
39756          * @event click
39757          * Fire after click
39758          * @param {Roo.bootstrap.DocumentSlider} this
39759          */
39760         "click" : true
39761     });
39762 };
39763
39764 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
39765     
39766     files : false,
39767     
39768     indicator : 0,
39769     
39770     getAutoCreate : function()
39771     {
39772         var cfg = {
39773             tag : 'div',
39774             cls : 'roo-document-slider',
39775             cn : [
39776                 {
39777                     tag : 'div',
39778                     cls : 'roo-document-slider-header',
39779                     cn : [
39780                         {
39781                             tag : 'div',
39782                             cls : 'roo-document-slider-header-title'
39783                         }
39784                     ]
39785                 },
39786                 {
39787                     tag : 'div',
39788                     cls : 'roo-document-slider-body',
39789                     cn : [
39790                         {
39791                             tag : 'div',
39792                             cls : 'roo-document-slider-prev',
39793                             cn : [
39794                                 {
39795                                     tag : 'i',
39796                                     cls : 'fa fa-chevron-left'
39797                                 }
39798                             ]
39799                         },
39800                         {
39801                             tag : 'div',
39802                             cls : 'roo-document-slider-thumb',
39803                             cn : [
39804                                 {
39805                                     tag : 'img',
39806                                     cls : 'roo-document-slider-image'
39807                                 }
39808                             ]
39809                         },
39810                         {
39811                             tag : 'div',
39812                             cls : 'roo-document-slider-next',
39813                             cn : [
39814                                 {
39815                                     tag : 'i',
39816                                     cls : 'fa fa-chevron-right'
39817                                 }
39818                             ]
39819                         }
39820                     ]
39821                 }
39822             ]
39823         };
39824         
39825         return cfg;
39826     },
39827     
39828     initEvents : function()
39829     {
39830         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
39831         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
39832         
39833         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
39834         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
39835         
39836         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
39837         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
39838         
39839         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
39840         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
39841         
39842         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
39843         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
39844         
39845         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
39846         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39847         
39848         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
39849         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39850         
39851         this.thumbEl.on('click', this.onClick, this);
39852         
39853         this.prevIndicator.on('click', this.prev, this);
39854         
39855         this.nextIndicator.on('click', this.next, this);
39856         
39857     },
39858     
39859     initial : function()
39860     {
39861         if(this.files.length){
39862             this.indicator = 1;
39863             this.update()
39864         }
39865         
39866         this.fireEvent('initial', this);
39867     },
39868     
39869     update : function()
39870     {
39871         this.imageEl.attr('src', this.files[this.indicator - 1]);
39872         
39873         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
39874         
39875         this.prevIndicator.show();
39876         
39877         if(this.indicator == 1){
39878             this.prevIndicator.hide();
39879         }
39880         
39881         this.nextIndicator.show();
39882         
39883         if(this.indicator == this.files.length){
39884             this.nextIndicator.hide();
39885         }
39886         
39887         this.thumbEl.scrollTo('top');
39888         
39889         this.fireEvent('update', this);
39890     },
39891     
39892     onClick : function(e)
39893     {
39894         e.preventDefault();
39895         
39896         this.fireEvent('click', this);
39897     },
39898     
39899     prev : function(e)
39900     {
39901         e.preventDefault();
39902         
39903         this.indicator = Math.max(1, this.indicator - 1);
39904         
39905         this.update();
39906     },
39907     
39908     next : function(e)
39909     {
39910         e.preventDefault();
39911         
39912         this.indicator = Math.min(this.files.length, this.indicator + 1);
39913         
39914         this.update();
39915     }
39916 });
39917 /*
39918  * - LGPL
39919  *
39920  * RadioSet
39921  *
39922  *
39923  */
39924
39925 /**
39926  * @class Roo.bootstrap.form.RadioSet
39927  * @extends Roo.bootstrap.form.Input
39928  * @children Roo.bootstrap.form.Radio
39929  * Bootstrap RadioSet class
39930  * @cfg {String} indicatorpos (left|right) default left
39931  * @cfg {Boolean} inline (true|false) inline the element (default true)
39932  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
39933  * @constructor
39934  * Create a new RadioSet
39935  * @param {Object} config The config object
39936  */
39937
39938 Roo.bootstrap.form.RadioSet = function(config){
39939     
39940     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
39941     
39942     this.radioes = [];
39943     
39944     Roo.bootstrap.form.RadioSet.register(this);
39945     
39946     this.addEvents({
39947         /**
39948         * @event check
39949         * Fires when the element is checked or unchecked.
39950         * @param {Roo.bootstrap.form.RadioSet} this This radio
39951         * @param {Roo.bootstrap.form.Radio} item The checked item
39952         */
39953        check : true,
39954        /**
39955         * @event click
39956         * Fires when the element is click.
39957         * @param {Roo.bootstrap.form.RadioSet} this This radio set
39958         * @param {Roo.bootstrap.form.Radio} item The checked item
39959         * @param {Roo.EventObject} e The event object
39960         */
39961        click : true
39962     });
39963     
39964 };
39965
39966 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
39967
39968     radioes : false,
39969     
39970     inline : true,
39971     
39972     weight : '',
39973     
39974     indicatorpos : 'left',
39975     
39976     getAutoCreate : function()
39977     {
39978         var label = {
39979             tag : 'label',
39980             cls : 'roo-radio-set-label',
39981             cn : [
39982                 {
39983                     tag : 'span',
39984                     html : this.fieldLabel
39985                 }
39986             ]
39987         };
39988         if (Roo.bootstrap.version == 3) {
39989             
39990             
39991             if(this.indicatorpos == 'left'){
39992                 label.cn.unshift({
39993                     tag : 'i',
39994                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
39995                     tooltip : 'This field is required'
39996                 });
39997             } else {
39998                 label.cn.push({
39999                     tag : 'i',
40000                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
40001                     tooltip : 'This field is required'
40002                 });
40003             }
40004         }
40005         var items = {
40006             tag : 'div',
40007             cls : 'roo-radio-set-items'
40008         };
40009         
40010         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
40011         
40012         if (align === 'left' && this.fieldLabel.length) {
40013             
40014             items = {
40015                 cls : "roo-radio-set-right", 
40016                 cn: [
40017                     items
40018                 ]
40019             };
40020             
40021             if(this.labelWidth > 12){
40022                 label.style = "width: " + this.labelWidth + 'px';
40023             }
40024             
40025             if(this.labelWidth < 13 && this.labelmd == 0){
40026                 this.labelmd = this.labelWidth;
40027             }
40028             
40029             if(this.labellg > 0){
40030                 label.cls += ' col-lg-' + this.labellg;
40031                 items.cls += ' col-lg-' + (12 - this.labellg);
40032             }
40033             
40034             if(this.labelmd > 0){
40035                 label.cls += ' col-md-' + this.labelmd;
40036                 items.cls += ' col-md-' + (12 - this.labelmd);
40037             }
40038             
40039             if(this.labelsm > 0){
40040                 label.cls += ' col-sm-' + this.labelsm;
40041                 items.cls += ' col-sm-' + (12 - this.labelsm);
40042             }
40043             
40044             if(this.labelxs > 0){
40045                 label.cls += ' col-xs-' + this.labelxs;
40046                 items.cls += ' col-xs-' + (12 - this.labelxs);
40047             }
40048         }
40049         
40050         var cfg = {
40051             tag : 'div',
40052             cls : 'roo-radio-set',
40053             cn : [
40054                 {
40055                     tag : 'input',
40056                     cls : 'roo-radio-set-input',
40057                     type : 'hidden',
40058                     name : this.name,
40059                     value : this.value ? this.value :  ''
40060                 },
40061                 label,
40062                 items
40063             ]
40064         };
40065         
40066         if(this.weight.length){
40067             cfg.cls += ' roo-radio-' + this.weight;
40068         }
40069         
40070         if(this.inline) {
40071             cfg.cls += ' roo-radio-set-inline';
40072         }
40073         
40074         var settings=this;
40075         ['xs','sm','md','lg'].map(function(size){
40076             if (settings[size]) {
40077                 cfg.cls += ' col-' + size + '-' + settings[size];
40078             }
40079         });
40080         
40081         return cfg;
40082         
40083     },
40084
40085     initEvents : function()
40086     {
40087         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
40088         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
40089         
40090         if(!this.fieldLabel.length){
40091             this.labelEl.hide();
40092         }
40093         
40094         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
40095         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
40096         
40097         this.indicator = this.indicatorEl();
40098         
40099         if(this.indicator){
40100             this.indicator.addClass('invisible');
40101         }
40102         
40103         this.originalValue = this.getValue();
40104         
40105     },
40106     
40107     inputEl: function ()
40108     {
40109         return this.el.select('.roo-radio-set-input', true).first();
40110     },
40111     
40112     getChildContainer : function()
40113     {
40114         return this.itemsEl;
40115     },
40116     
40117     register : function(item)
40118     {
40119         this.radioes.push(item);
40120         
40121     },
40122     
40123     validate : function()
40124     {   
40125         if(this.getVisibilityEl().hasClass('hidden')){
40126             return true;
40127         }
40128         
40129         var valid = false;
40130         
40131         Roo.each(this.radioes, function(i){
40132             if(!i.checked){
40133                 return;
40134             }
40135             
40136             valid = true;
40137             return false;
40138         });
40139         
40140         if(this.allowBlank) {
40141             return true;
40142         }
40143         
40144         if(this.disabled || valid){
40145             this.markValid();
40146             return true;
40147         }
40148         
40149         this.markInvalid();
40150         return false;
40151         
40152     },
40153     
40154     markValid : function()
40155     {
40156         if(this.labelEl.isVisible(true) && this.indicatorEl()){
40157             this.indicatorEl().removeClass('visible');
40158             this.indicatorEl().addClass('invisible');
40159         }
40160         
40161         
40162         if (Roo.bootstrap.version == 3) {
40163             this.el.removeClass([this.invalidClass, this.validClass]);
40164             this.el.addClass(this.validClass);
40165         } else {
40166             this.el.removeClass(['is-invalid','is-valid']);
40167             this.el.addClass(['is-valid']);
40168         }
40169         this.fireEvent('valid', this);
40170     },
40171     
40172     markInvalid : function(msg)
40173     {
40174         if(this.allowBlank || this.disabled){
40175             return;
40176         }
40177         
40178         if(this.labelEl.isVisible(true) && this.indicatorEl()){
40179             this.indicatorEl().removeClass('invisible');
40180             this.indicatorEl().addClass('visible');
40181         }
40182         if (Roo.bootstrap.version == 3) {
40183             this.el.removeClass([this.invalidClass, this.validClass]);
40184             this.el.addClass(this.invalidClass);
40185         } else {
40186             this.el.removeClass(['is-invalid','is-valid']);
40187             this.el.addClass(['is-invalid']);
40188         }
40189         
40190         this.fireEvent('invalid', this, msg);
40191         
40192     },
40193     
40194     setValue : function(v, suppressEvent)
40195     {   
40196         if(this.value === v){
40197             return;
40198         }
40199         
40200         this.value = v;
40201         
40202         if(this.rendered){
40203             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40204         }
40205         
40206         Roo.each(this.radioes, function(i){
40207             i.checked = false;
40208             i.el.removeClass('checked');
40209         });
40210         
40211         Roo.each(this.radioes, function(i){
40212             
40213             if(i.value === v || i.value.toString() === v.toString()){
40214                 i.checked = true;
40215                 i.el.addClass('checked');
40216                 
40217                 if(suppressEvent !== true){
40218                     this.fireEvent('check', this, i);
40219                 }
40220                 
40221                 return false;
40222             }
40223             
40224         }, this);
40225         
40226         this.validate();
40227     },
40228     
40229     clearInvalid : function(){
40230         
40231         if(!this.el || this.preventMark){
40232             return;
40233         }
40234         
40235         this.el.removeClass([this.invalidClass]);
40236         
40237         this.fireEvent('valid', this);
40238     }
40239     
40240 });
40241
40242 Roo.apply(Roo.bootstrap.form.RadioSet, {
40243     
40244     groups: {},
40245     
40246     register : function(set)
40247     {
40248         this.groups[set.name] = set;
40249     },
40250     
40251     get: function(name) 
40252     {
40253         if (typeof(this.groups[name]) == 'undefined') {
40254             return false;
40255         }
40256         
40257         return this.groups[name] ;
40258     }
40259     
40260 });
40261 /*
40262  * Based on:
40263  * Ext JS Library 1.1.1
40264  * Copyright(c) 2006-2007, Ext JS, LLC.
40265  *
40266  * Originally Released Under LGPL - original licence link has changed is not relivant.
40267  *
40268  * Fork - LGPL
40269  * <script type="text/javascript">
40270  */
40271
40272
40273 /**
40274  * @class Roo.bootstrap.SplitBar
40275  * @extends Roo.util.Observable
40276  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
40277  * <br><br>
40278  * Usage:
40279  * <pre><code>
40280 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
40281                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
40282 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
40283 split.minSize = 100;
40284 split.maxSize = 600;
40285 split.animate = true;
40286 split.on('moved', splitterMoved);
40287 </code></pre>
40288  * @constructor
40289  * Create a new SplitBar
40290  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
40291  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
40292  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40293  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
40294                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
40295                         position of the SplitBar).
40296  */
40297 Roo.bootstrap.SplitBar = function(cfg){
40298     
40299     /** @private */
40300     
40301     //{
40302     //  dragElement : elm
40303     //  resizingElement: el,
40304         // optional..
40305     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
40306     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
40307         // existingProxy ???
40308     //}
40309     
40310     this.el = Roo.get(cfg.dragElement, true);
40311     this.el.dom.unselectable = "on";
40312     /** @private */
40313     this.resizingEl = Roo.get(cfg.resizingElement, true);
40314
40315     /**
40316      * @private
40317      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40318      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
40319      * @type Number
40320      */
40321     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
40322     
40323     /**
40324      * The minimum size of the resizing element. (Defaults to 0)
40325      * @type Number
40326      */
40327     this.minSize = 0;
40328     
40329     /**
40330      * The maximum size of the resizing element. (Defaults to 2000)
40331      * @type Number
40332      */
40333     this.maxSize = 2000;
40334     
40335     /**
40336      * Whether to animate the transition to the new size
40337      * @type Boolean
40338      */
40339     this.animate = false;
40340     
40341     /**
40342      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
40343      * @type Boolean
40344      */
40345     this.useShim = false;
40346     
40347     /** @private */
40348     this.shim = null;
40349     
40350     if(!cfg.existingProxy){
40351         /** @private */
40352         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
40353     }else{
40354         this.proxy = Roo.get(cfg.existingProxy).dom;
40355     }
40356     /** @private */
40357     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
40358     
40359     /** @private */
40360     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
40361     
40362     /** @private */
40363     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
40364     
40365     /** @private */
40366     this.dragSpecs = {};
40367     
40368     /**
40369      * @private The adapter to use to positon and resize elements
40370      */
40371     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40372     this.adapter.init(this);
40373     
40374     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40375         /** @private */
40376         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
40377         this.el.addClass("roo-splitbar-h");
40378     }else{
40379         /** @private */
40380         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
40381         this.el.addClass("roo-splitbar-v");
40382     }
40383     
40384     this.addEvents({
40385         /**
40386          * @event resize
40387          * Fires when the splitter is moved (alias for {@link #event-moved})
40388          * @param {Roo.bootstrap.SplitBar} this
40389          * @param {Number} newSize the new width or height
40390          */
40391         "resize" : true,
40392         /**
40393          * @event moved
40394          * Fires when the splitter is moved
40395          * @param {Roo.bootstrap.SplitBar} this
40396          * @param {Number} newSize the new width or height
40397          */
40398         "moved" : true,
40399         /**
40400          * @event beforeresize
40401          * Fires before the splitter is dragged
40402          * @param {Roo.bootstrap.SplitBar} this
40403          */
40404         "beforeresize" : true,
40405
40406         "beforeapply" : true
40407     });
40408
40409     Roo.util.Observable.call(this);
40410 };
40411
40412 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
40413     onStartProxyDrag : function(x, y){
40414         this.fireEvent("beforeresize", this);
40415         if(!this.overlay){
40416             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
40417             o.unselectable();
40418             o.enableDisplayMode("block");
40419             // all splitbars share the same overlay
40420             Roo.bootstrap.SplitBar.prototype.overlay = o;
40421         }
40422         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
40423         this.overlay.show();
40424         Roo.get(this.proxy).setDisplayed("block");
40425         var size = this.adapter.getElementSize(this);
40426         this.activeMinSize = this.getMinimumSize();;
40427         this.activeMaxSize = this.getMaximumSize();;
40428         var c1 = size - this.activeMinSize;
40429         var c2 = Math.max(this.activeMaxSize - size, 0);
40430         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40431             this.dd.resetConstraints();
40432             this.dd.setXConstraint(
40433                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
40434                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
40435             );
40436             this.dd.setYConstraint(0, 0);
40437         }else{
40438             this.dd.resetConstraints();
40439             this.dd.setXConstraint(0, 0);
40440             this.dd.setYConstraint(
40441                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
40442                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
40443             );
40444          }
40445         this.dragSpecs.startSize = size;
40446         this.dragSpecs.startPoint = [x, y];
40447         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
40448     },
40449     
40450     /** 
40451      * @private Called after the drag operation by the DDProxy
40452      */
40453     onEndProxyDrag : function(e){
40454         Roo.get(this.proxy).setDisplayed(false);
40455         var endPoint = Roo.lib.Event.getXY(e);
40456         if(this.overlay){
40457             this.overlay.hide();
40458         }
40459         var newSize;
40460         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40461             newSize = this.dragSpecs.startSize + 
40462                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
40463                     endPoint[0] - this.dragSpecs.startPoint[0] :
40464                     this.dragSpecs.startPoint[0] - endPoint[0]
40465                 );
40466         }else{
40467             newSize = this.dragSpecs.startSize + 
40468                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
40469                     endPoint[1] - this.dragSpecs.startPoint[1] :
40470                     this.dragSpecs.startPoint[1] - endPoint[1]
40471                 );
40472         }
40473         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
40474         if(newSize != this.dragSpecs.startSize){
40475             if(this.fireEvent('beforeapply', this, newSize) !== false){
40476                 this.adapter.setElementSize(this, newSize);
40477                 this.fireEvent("moved", this, newSize);
40478                 this.fireEvent("resize", this, newSize);
40479             }
40480         }
40481     },
40482     
40483     /**
40484      * Get the adapter this SplitBar uses
40485      * @return The adapter object
40486      */
40487     getAdapter : function(){
40488         return this.adapter;
40489     },
40490     
40491     /**
40492      * Set the adapter this SplitBar uses
40493      * @param {Object} adapter A SplitBar adapter object
40494      */
40495     setAdapter : function(adapter){
40496         this.adapter = adapter;
40497         this.adapter.init(this);
40498     },
40499     
40500     /**
40501      * Gets the minimum size for the resizing element
40502      * @return {Number} The minimum size
40503      */
40504     getMinimumSize : function(){
40505         return this.minSize;
40506     },
40507     
40508     /**
40509      * Sets the minimum size for the resizing element
40510      * @param {Number} minSize The minimum size
40511      */
40512     setMinimumSize : function(minSize){
40513         this.minSize = minSize;
40514     },
40515     
40516     /**
40517      * Gets the maximum size for the resizing element
40518      * @return {Number} The maximum size
40519      */
40520     getMaximumSize : function(){
40521         return this.maxSize;
40522     },
40523     
40524     /**
40525      * Sets the maximum size for the resizing element
40526      * @param {Number} maxSize The maximum size
40527      */
40528     setMaximumSize : function(maxSize){
40529         this.maxSize = maxSize;
40530     },
40531     
40532     /**
40533      * Sets the initialize size for the resizing element
40534      * @param {Number} size The initial size
40535      */
40536     setCurrentSize : function(size){
40537         var oldAnimate = this.animate;
40538         this.animate = false;
40539         this.adapter.setElementSize(this, size);
40540         this.animate = oldAnimate;
40541     },
40542     
40543     /**
40544      * Destroy this splitbar. 
40545      * @param {Boolean} removeEl True to remove the element
40546      */
40547     destroy : function(removeEl){
40548         if(this.shim){
40549             this.shim.remove();
40550         }
40551         this.dd.unreg();
40552         this.proxy.parentNode.removeChild(this.proxy);
40553         if(removeEl){
40554             this.el.remove();
40555         }
40556     }
40557 });
40558
40559 /**
40560  * @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.
40561  */
40562 Roo.bootstrap.SplitBar.createProxy = function(dir){
40563     var proxy = new Roo.Element(document.createElement("div"));
40564     proxy.unselectable();
40565     var cls = 'roo-splitbar-proxy';
40566     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
40567     document.body.appendChild(proxy.dom);
40568     return proxy.dom;
40569 };
40570
40571 /** 
40572  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
40573  * Default Adapter. It assumes the splitter and resizing element are not positioned
40574  * elements and only gets/sets the width of the element. Generally used for table based layouts.
40575  */
40576 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
40577 };
40578
40579 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
40580     // do nothing for now
40581     init : function(s){
40582     
40583     },
40584     /**
40585      * Called before drag operations to get the current size of the resizing element. 
40586      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40587      */
40588      getElementSize : function(s){
40589         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40590             return s.resizingEl.getWidth();
40591         }else{
40592             return s.resizingEl.getHeight();
40593         }
40594     },
40595     
40596     /**
40597      * Called after drag operations to set the size of the resizing element.
40598      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40599      * @param {Number} newSize The new size to set
40600      * @param {Function} onComplete A function to be invoked when resizing is complete
40601      */
40602     setElementSize : function(s, newSize, onComplete){
40603         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40604             if(!s.animate){
40605                 s.resizingEl.setWidth(newSize);
40606                 if(onComplete){
40607                     onComplete(s, newSize);
40608                 }
40609             }else{
40610                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
40611             }
40612         }else{
40613             
40614             if(!s.animate){
40615                 s.resizingEl.setHeight(newSize);
40616                 if(onComplete){
40617                     onComplete(s, newSize);
40618                 }
40619             }else{
40620                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
40621             }
40622         }
40623     }
40624 };
40625
40626 /** 
40627  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
40628  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
40629  * Adapter that  moves the splitter element to align with the resized sizing element. 
40630  * Used with an absolute positioned SplitBar.
40631  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
40632  * document.body, make sure you assign an id to the body element.
40633  */
40634 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
40635     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40636     this.container = Roo.get(container);
40637 };
40638
40639 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
40640     init : function(s){
40641         this.basic.init(s);
40642     },
40643     
40644     getElementSize : function(s){
40645         return this.basic.getElementSize(s);
40646     },
40647     
40648     setElementSize : function(s, newSize, onComplete){
40649         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
40650     },
40651     
40652     moveSplitter : function(s){
40653         var yes = Roo.bootstrap.SplitBar;
40654         switch(s.placement){
40655             case yes.LEFT:
40656                 s.el.setX(s.resizingEl.getRight());
40657                 break;
40658             case yes.RIGHT:
40659                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
40660                 break;
40661             case yes.TOP:
40662                 s.el.setY(s.resizingEl.getBottom());
40663                 break;
40664             case yes.BOTTOM:
40665                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
40666                 break;
40667         }
40668     }
40669 };
40670
40671 /**
40672  * Orientation constant - Create a vertical SplitBar
40673  * @static
40674  * @type Number
40675  */
40676 Roo.bootstrap.SplitBar.VERTICAL = 1;
40677
40678 /**
40679  * Orientation constant - Create a horizontal SplitBar
40680  * @static
40681  * @type Number
40682  */
40683 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
40684
40685 /**
40686  * Placement constant - The resizing element is to the left of the splitter element
40687  * @static
40688  * @type Number
40689  */
40690 Roo.bootstrap.SplitBar.LEFT = 1;
40691
40692 /**
40693  * Placement constant - The resizing element is to the right of the splitter element
40694  * @static
40695  * @type Number
40696  */
40697 Roo.bootstrap.SplitBar.RIGHT = 2;
40698
40699 /**
40700  * Placement constant - The resizing element is positioned above the splitter element
40701  * @static
40702  * @type Number
40703  */
40704 Roo.bootstrap.SplitBar.TOP = 3;
40705
40706 /**
40707  * Placement constant - The resizing element is positioned under splitter element
40708  * @static
40709  * @type Number
40710  */
40711 Roo.bootstrap.SplitBar.BOTTOM = 4;
40712 /*
40713  * Based on:
40714  * Ext JS Library 1.1.1
40715  * Copyright(c) 2006-2007, Ext JS, LLC.
40716  *
40717  * Originally Released Under LGPL - original licence link has changed is not relivant.
40718  *
40719  * Fork - LGPL
40720  * <script type="text/javascript">
40721  */
40722
40723 /**
40724  * @class Roo.bootstrap.layout.Manager
40725  * @extends Roo.bootstrap.Component
40726  * @abstract
40727  * Base class for layout managers.
40728  */
40729 Roo.bootstrap.layout.Manager = function(config)
40730 {
40731     this.monitorWindowResize = true; // do this before we apply configuration.
40732     
40733     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
40734
40735
40736
40737
40738
40739     /** false to disable window resize monitoring @type Boolean */
40740     
40741     this.regions = {};
40742     this.addEvents({
40743         /**
40744          * @event layout
40745          * Fires when a layout is performed.
40746          * @param {Roo.LayoutManager} this
40747          */
40748         "layout" : true,
40749         /**
40750          * @event regionresized
40751          * Fires when the user resizes a region.
40752          * @param {Roo.LayoutRegion} region The resized region
40753          * @param {Number} newSize The new size (width for east/west, height for north/south)
40754          */
40755         "regionresized" : true,
40756         /**
40757          * @event regioncollapsed
40758          * Fires when a region is collapsed.
40759          * @param {Roo.LayoutRegion} region The collapsed region
40760          */
40761         "regioncollapsed" : true,
40762         /**
40763          * @event regionexpanded
40764          * Fires when a region is expanded.
40765          * @param {Roo.LayoutRegion} region The expanded region
40766          */
40767         "regionexpanded" : true
40768     });
40769     this.updating = false;
40770
40771     if (config.el) {
40772         this.el = Roo.get(config.el);
40773         this.initEvents();
40774     }
40775
40776 };
40777
40778 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
40779
40780
40781     regions : null,
40782
40783     monitorWindowResize : true,
40784
40785
40786     updating : false,
40787
40788
40789     onRender : function(ct, position)
40790     {
40791         if(!this.el){
40792             this.el = Roo.get(ct);
40793             this.initEvents();
40794         }
40795         //this.fireEvent('render',this);
40796     },
40797
40798
40799     initEvents: function()
40800     {
40801
40802
40803         // ie scrollbar fix
40804         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
40805             document.body.scroll = "no";
40806         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
40807             this.el.position('relative');
40808         }
40809         this.id = this.el.id;
40810         this.el.addClass("roo-layout-container");
40811         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
40812         if(this.el.dom != document.body ) {
40813             this.el.on('resize', this.layout,this);
40814             this.el.on('show', this.layout,this);
40815         }
40816
40817     },
40818
40819     /**
40820      * Returns true if this layout is currently being updated
40821      * @return {Boolean}
40822      */
40823     isUpdating : function(){
40824         return this.updating;
40825     },
40826
40827     /**
40828      * Suspend the LayoutManager from doing auto-layouts while
40829      * making multiple add or remove calls
40830      */
40831     beginUpdate : function(){
40832         this.updating = true;
40833     },
40834
40835     /**
40836      * Restore auto-layouts and optionally disable the manager from performing a layout
40837      * @param {Boolean} noLayout true to disable a layout update
40838      */
40839     endUpdate : function(noLayout){
40840         this.updating = false;
40841         if(!noLayout){
40842             this.layout();
40843         }
40844     },
40845
40846     layout: function(){
40847         // abstract...
40848     },
40849
40850     onRegionResized : function(region, newSize){
40851         this.fireEvent("regionresized", region, newSize);
40852         this.layout();
40853     },
40854
40855     onRegionCollapsed : function(region){
40856         this.fireEvent("regioncollapsed", region);
40857     },
40858
40859     onRegionExpanded : function(region){
40860         this.fireEvent("regionexpanded", region);
40861     },
40862
40863     /**
40864      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
40865      * performs box-model adjustments.
40866      * @return {Object} The size as an object {width: (the width), height: (the height)}
40867      */
40868     getViewSize : function()
40869     {
40870         var size;
40871         if(this.el.dom != document.body){
40872             size = this.el.getSize();
40873         }else{
40874             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
40875         }
40876         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
40877         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40878         return size;
40879     },
40880
40881     /**
40882      * Returns the Element this layout is bound to.
40883      * @return {Roo.Element}
40884      */
40885     getEl : function(){
40886         return this.el;
40887     },
40888
40889     /**
40890      * Returns the specified region.
40891      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
40892      * @return {Roo.LayoutRegion}
40893      */
40894     getRegion : function(target){
40895         return this.regions[target.toLowerCase()];
40896     },
40897
40898     onWindowResize : function(){
40899         if(this.monitorWindowResize){
40900             this.layout();
40901         }
40902     }
40903 });
40904 /*
40905  * Based on:
40906  * Ext JS Library 1.1.1
40907  * Copyright(c) 2006-2007, Ext JS, LLC.
40908  *
40909  * Originally Released Under LGPL - original licence link has changed is not relivant.
40910  *
40911  * Fork - LGPL
40912  * <script type="text/javascript">
40913  */
40914 /**
40915  * @class Roo.bootstrap.layout.Border
40916  * @extends Roo.bootstrap.layout.Manager
40917  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
40918  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
40919  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
40920  * please see: examples/bootstrap/nested.html<br><br>
40921  
40922 <b>The container the layout is rendered into can be either the body element or any other element.
40923 If it is not the body element, the container needs to either be an absolute positioned element,
40924 or you will need to add "position:relative" to the css of the container.  You will also need to specify
40925 the container size if it is not the body element.</b>
40926
40927 * @constructor
40928 * Create a new Border
40929 * @param {Object} config Configuration options
40930  */
40931 Roo.bootstrap.layout.Border = function(config){
40932     config = config || {};
40933     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
40934     
40935     
40936     
40937     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40938         if(config[region]){
40939             config[region].region = region;
40940             this.addRegion(config[region]);
40941         }
40942     },this);
40943     
40944 };
40945
40946 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
40947
40948 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
40949     
40950         /**
40951          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
40952          */
40953         /**
40954          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
40955          */
40956         /**
40957          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
40958          */
40959         /**
40960          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
40961          */
40962         /**
40963          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
40964          */
40965         
40966         
40967         
40968         
40969     parent : false, // this might point to a 'nest' or a ???
40970     
40971     /**
40972      * Creates and adds a new region if it doesn't already exist.
40973      * @param {String} target The target region key (north, south, east, west or center).
40974      * @param {Object} config The regions config object
40975      * @return {BorderLayoutRegion} The new region
40976      */
40977     addRegion : function(config)
40978     {
40979         if(!this.regions[config.region]){
40980             var r = this.factory(config);
40981             this.bindRegion(r);
40982         }
40983         return this.regions[config.region];
40984     },
40985
40986     // private (kinda)
40987     bindRegion : function(r){
40988         this.regions[r.config.region] = r;
40989         
40990         r.on("visibilitychange",    this.layout, this);
40991         r.on("paneladded",          this.layout, this);
40992         r.on("panelremoved",        this.layout, this);
40993         r.on("invalidated",         this.layout, this);
40994         r.on("resized",             this.onRegionResized, this);
40995         r.on("collapsed",           this.onRegionCollapsed, this);
40996         r.on("expanded",            this.onRegionExpanded, this);
40997     },
40998
40999     /**
41000      * Performs a layout update.
41001      */
41002     layout : function()
41003     {
41004         if(this.updating) {
41005             return;
41006         }
41007         
41008         // render all the rebions if they have not been done alreayd?
41009         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
41010             if(this.regions[region] && !this.regions[region].bodyEl){
41011                 this.regions[region].onRender(this.el)
41012             }
41013         },this);
41014         
41015         var size = this.getViewSize();
41016         var w = size.width;
41017         var h = size.height;
41018         var centerW = w;
41019         var centerH = h;
41020         var centerY = 0;
41021         var centerX = 0;
41022         //var x = 0, y = 0;
41023
41024         var rs = this.regions;
41025         var north = rs["north"];
41026         var south = rs["south"]; 
41027         var west = rs["west"];
41028         var east = rs["east"];
41029         var center = rs["center"];
41030         //if(this.hideOnLayout){ // not supported anymore
41031             //c.el.setStyle("display", "none");
41032         //}
41033         if(north && north.isVisible()){
41034             var b = north.getBox();
41035             var m = north.getMargins();
41036             b.width = w - (m.left+m.right);
41037             b.x = m.left;
41038             b.y = m.top;
41039             centerY = b.height + b.y + m.bottom;
41040             centerH -= centerY;
41041             north.updateBox(this.safeBox(b));
41042         }
41043         if(south && south.isVisible()){
41044             var b = south.getBox();
41045             var m = south.getMargins();
41046             b.width = w - (m.left+m.right);
41047             b.x = m.left;
41048             var totalHeight = (b.height + m.top + m.bottom);
41049             b.y = h - totalHeight + m.top;
41050             centerH -= totalHeight;
41051             south.updateBox(this.safeBox(b));
41052         }
41053         if(west && west.isVisible()){
41054             var b = west.getBox();
41055             var m = west.getMargins();
41056             b.height = centerH - (m.top+m.bottom);
41057             b.x = m.left;
41058             b.y = centerY + m.top;
41059             var totalWidth = (b.width + m.left + m.right);
41060             centerX += totalWidth;
41061             centerW -= totalWidth;
41062             west.updateBox(this.safeBox(b));
41063         }
41064         if(east && east.isVisible()){
41065             var b = east.getBox();
41066             var m = east.getMargins();
41067             b.height = centerH - (m.top+m.bottom);
41068             var totalWidth = (b.width + m.left + m.right);
41069             b.x = w - totalWidth + m.left;
41070             b.y = centerY + m.top;
41071             centerW -= totalWidth;
41072             east.updateBox(this.safeBox(b));
41073         }
41074         if(center){
41075             var m = center.getMargins();
41076             var centerBox = {
41077                 x: centerX + m.left,
41078                 y: centerY + m.top,
41079                 width: centerW - (m.left+m.right),
41080                 height: centerH - (m.top+m.bottom)
41081             };
41082             //if(this.hideOnLayout){
41083                 //center.el.setStyle("display", "block");
41084             //}
41085             center.updateBox(this.safeBox(centerBox));
41086         }
41087         this.el.repaint();
41088         this.fireEvent("layout", this);
41089     },
41090
41091     // private
41092     safeBox : function(box){
41093         box.width = Math.max(0, box.width);
41094         box.height = Math.max(0, box.height);
41095         return box;
41096     },
41097
41098     /**
41099      * Adds a ContentPanel (or subclass) to this layout.
41100      * @param {String} target The target region key (north, south, east, west or center).
41101      * @param {Roo.ContentPanel} panel The panel to add
41102      * @return {Roo.ContentPanel} The added panel
41103      */
41104     add : function(target, panel){
41105          
41106         target = target.toLowerCase();
41107         return this.regions[target].add(panel);
41108     },
41109
41110     /**
41111      * Remove a ContentPanel (or subclass) to this layout.
41112      * @param {String} target The target region key (north, south, east, west or center).
41113      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
41114      * @return {Roo.ContentPanel} The removed panel
41115      */
41116     remove : function(target, panel){
41117         target = target.toLowerCase();
41118         return this.regions[target].remove(panel);
41119     },
41120
41121     /**
41122      * Searches all regions for a panel with the specified id
41123      * @param {String} panelId
41124      * @return {Roo.ContentPanel} The panel or null if it wasn't found
41125      */
41126     findPanel : function(panelId){
41127         var rs = this.regions;
41128         for(var target in rs){
41129             if(typeof rs[target] != "function"){
41130                 var p = rs[target].getPanel(panelId);
41131                 if(p){
41132                     return p;
41133                 }
41134             }
41135         }
41136         return null;
41137     },
41138
41139     /**
41140      * Searches all regions for a panel with the specified id and activates (shows) it.
41141      * @param {String/ContentPanel} panelId The panels id or the panel itself
41142      * @return {Roo.ContentPanel} The shown panel or null
41143      */
41144     showPanel : function(panelId) {
41145       var rs = this.regions;
41146       for(var target in rs){
41147          var r = rs[target];
41148          if(typeof r != "function"){
41149             if(r.hasPanel(panelId)){
41150                return r.showPanel(panelId);
41151             }
41152          }
41153       }
41154       return null;
41155    },
41156
41157    /**
41158      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
41159      * @param {Roo.state.Provider} provider (optional) An alternate state provider
41160      */
41161    /*
41162     restoreState : function(provider){
41163         if(!provider){
41164             provider = Roo.state.Manager;
41165         }
41166         var sm = new Roo.LayoutStateManager();
41167         sm.init(this, provider);
41168     },
41169 */
41170  
41171  
41172     /**
41173      * Adds a xtype elements to the layout.
41174      * <pre><code>
41175
41176 layout.addxtype({
41177        xtype : 'ContentPanel',
41178        region: 'west',
41179        items: [ .... ]
41180    }
41181 );
41182
41183 layout.addxtype({
41184         xtype : 'NestedLayoutPanel',
41185         region: 'west',
41186         layout: {
41187            center: { },
41188            west: { }   
41189         },
41190         items : [ ... list of content panels or nested layout panels.. ]
41191    }
41192 );
41193 </code></pre>
41194      * @param {Object} cfg Xtype definition of item to add.
41195      */
41196     addxtype : function(cfg)
41197     {
41198         // basically accepts a pannel...
41199         // can accept a layout region..!?!?
41200         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
41201         
41202         
41203         // theory?  children can only be panels??
41204         
41205         //if (!cfg.xtype.match(/Panel$/)) {
41206         //    return false;
41207         //}
41208         var ret = false;
41209         
41210         if (typeof(cfg.region) == 'undefined') {
41211             Roo.log("Failed to add Panel, region was not set");
41212             Roo.log(cfg);
41213             return false;
41214         }
41215         var region = cfg.region;
41216         delete cfg.region;
41217         
41218           
41219         var xitems = [];
41220         if (cfg.items) {
41221             xitems = cfg.items;
41222             delete cfg.items;
41223         }
41224         var nb = false;
41225         
41226         if ( region == 'center') {
41227             Roo.log("Center: " + cfg.title);
41228         }
41229         
41230         
41231         switch(cfg.xtype) 
41232         {
41233             case 'Content':  // ContentPanel (el, cfg)
41234             case 'Scroll':  // ContentPanel (el, cfg)
41235             case 'View': 
41236                 cfg.autoCreate = cfg.autoCreate || true;
41237                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41238                 //} else {
41239                 //    var el = this.el.createChild();
41240                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
41241                 //}
41242                 
41243                 this.add(region, ret);
41244                 break;
41245             
41246             /*
41247             case 'TreePanel': // our new panel!
41248                 cfg.el = this.el.createChild();
41249                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41250                 this.add(region, ret);
41251                 break;
41252             */
41253             
41254             case 'Nest': 
41255                 // create a new Layout (which is  a Border Layout...
41256                 
41257                 var clayout = cfg.layout;
41258                 clayout.el  = this.el.createChild();
41259                 clayout.items   = clayout.items  || [];
41260                 
41261                 delete cfg.layout;
41262                 
41263                 // replace this exitems with the clayout ones..
41264                 xitems = clayout.items;
41265                  
41266                 // force background off if it's in center...
41267                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
41268                     cfg.background = false;
41269                 }
41270                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
41271                 
41272                 
41273                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41274                 //console.log('adding nested layout panel '  + cfg.toSource());
41275                 this.add(region, ret);
41276                 nb = {}; /// find first...
41277                 break;
41278             
41279             case 'Grid':
41280                 
41281                 // needs grid and region
41282                 
41283                 //var el = this.getRegion(region).el.createChild();
41284                 /*
41285                  *var el = this.el.createChild();
41286                 // create the grid first...
41287                 cfg.grid.container = el;
41288                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
41289                 */
41290                 
41291                 if (region == 'center' && this.active ) {
41292                     cfg.background = false;
41293                 }
41294                 
41295                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41296                 
41297                 this.add(region, ret);
41298                 /*
41299                 if (cfg.background) {
41300                     // render grid on panel activation (if panel background)
41301                     ret.on('activate', function(gp) {
41302                         if (!gp.grid.rendered) {
41303                     //        gp.grid.render(el);
41304                         }
41305                     });
41306                 } else {
41307                   //  cfg.grid.render(el);
41308                 }
41309                 */
41310                 break;
41311            
41312            
41313             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
41314                 // it was the old xcomponent building that caused this before.
41315                 // espeically if border is the top element in the tree.
41316                 ret = this;
41317                 break; 
41318                 
41319                     
41320                 
41321                 
41322                 
41323             default:
41324                 /*
41325                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
41326                     
41327                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41328                     this.add(region, ret);
41329                 } else {
41330                 */
41331                     Roo.log(cfg);
41332                     throw "Can not add '" + cfg.xtype + "' to Border";
41333                     return null;
41334              
41335                                 
41336              
41337         }
41338         this.beginUpdate();
41339         // add children..
41340         var region = '';
41341         var abn = {};
41342         Roo.each(xitems, function(i)  {
41343             region = nb && i.region ? i.region : false;
41344             
41345             var add = ret.addxtype(i);
41346            
41347             if (region) {
41348                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
41349                 if (!i.background) {
41350                     abn[region] = nb[region] ;
41351                 }
41352             }
41353             
41354         });
41355         this.endUpdate();
41356
41357         // make the last non-background panel active..
41358         //if (nb) { Roo.log(abn); }
41359         if (nb) {
41360             
41361             for(var r in abn) {
41362                 region = this.getRegion(r);
41363                 if (region) {
41364                     // tried using nb[r], but it does not work..
41365                      
41366                     region.showPanel(abn[r]);
41367                    
41368                 }
41369             }
41370         }
41371         return ret;
41372         
41373     },
41374     
41375     
41376 // private
41377     factory : function(cfg)
41378     {
41379         
41380         var validRegions = Roo.bootstrap.layout.Border.regions;
41381
41382         var target = cfg.region;
41383         cfg.mgr = this;
41384         
41385         var r = Roo.bootstrap.layout;
41386         Roo.log(target);
41387         switch(target){
41388             case "north":
41389                 return new r.North(cfg);
41390             case "south":
41391                 return new r.South(cfg);
41392             case "east":
41393                 return new r.East(cfg);
41394             case "west":
41395                 return new r.West(cfg);
41396             case "center":
41397                 return new r.Center(cfg);
41398         }
41399         throw 'Layout region "'+target+'" not supported.';
41400     }
41401     
41402     
41403 });
41404  /*
41405  * Based on:
41406  * Ext JS Library 1.1.1
41407  * Copyright(c) 2006-2007, Ext JS, LLC.
41408  *
41409  * Originally Released Under LGPL - original licence link has changed is not relivant.
41410  *
41411  * Fork - LGPL
41412  * <script type="text/javascript">
41413  */
41414  
41415 /**
41416  * @class Roo.bootstrap.layout.Basic
41417  * @extends Roo.util.Observable
41418  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
41419  * and does not have a titlebar, tabs or any other features. All it does is size and position 
41420  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
41421  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41422  * @cfg {string}   region  the region that it inhabits..
41423  * @cfg {bool}   skipConfig skip config?
41424  * 
41425
41426  */
41427 Roo.bootstrap.layout.Basic = function(config){
41428     
41429     this.mgr = config.mgr;
41430     
41431     this.position = config.region;
41432     
41433     var skipConfig = config.skipConfig;
41434     
41435     this.events = {
41436         /**
41437          * @scope Roo.BasicLayoutRegion
41438          */
41439         
41440         /**
41441          * @event beforeremove
41442          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
41443          * @param {Roo.LayoutRegion} this
41444          * @param {Roo.ContentPanel} panel The panel
41445          * @param {Object} e The cancel event object
41446          */
41447         "beforeremove" : true,
41448         /**
41449          * @event invalidated
41450          * Fires when the layout for this region is changed.
41451          * @param {Roo.LayoutRegion} this
41452          */
41453         "invalidated" : true,
41454         /**
41455          * @event visibilitychange
41456          * Fires when this region is shown or hidden 
41457          * @param {Roo.LayoutRegion} this
41458          * @param {Boolean} visibility true or false
41459          */
41460         "visibilitychange" : true,
41461         /**
41462          * @event paneladded
41463          * Fires when a panel is added. 
41464          * @param {Roo.LayoutRegion} this
41465          * @param {Roo.ContentPanel} panel The panel
41466          */
41467         "paneladded" : true,
41468         /**
41469          * @event panelremoved
41470          * Fires when a panel is removed. 
41471          * @param {Roo.LayoutRegion} this
41472          * @param {Roo.ContentPanel} panel The panel
41473          */
41474         "panelremoved" : true,
41475         /**
41476          * @event beforecollapse
41477          * Fires when this region before collapse.
41478          * @param {Roo.LayoutRegion} this
41479          */
41480         "beforecollapse" : true,
41481         /**
41482          * @event collapsed
41483          * Fires when this region is collapsed.
41484          * @param {Roo.LayoutRegion} this
41485          */
41486         "collapsed" : true,
41487         /**
41488          * @event expanded
41489          * Fires when this region is expanded.
41490          * @param {Roo.LayoutRegion} this
41491          */
41492         "expanded" : true,
41493         /**
41494          * @event slideshow
41495          * Fires when this region is slid into view.
41496          * @param {Roo.LayoutRegion} this
41497          */
41498         "slideshow" : true,
41499         /**
41500          * @event slidehide
41501          * Fires when this region slides out of view. 
41502          * @param {Roo.LayoutRegion} this
41503          */
41504         "slidehide" : true,
41505         /**
41506          * @event panelactivated
41507          * Fires when a panel is activated. 
41508          * @param {Roo.LayoutRegion} this
41509          * @param {Roo.ContentPanel} panel The activated panel
41510          */
41511         "panelactivated" : true,
41512         /**
41513          * @event resized
41514          * Fires when the user resizes this region. 
41515          * @param {Roo.LayoutRegion} this
41516          * @param {Number} newSize The new size (width for east/west, height for north/south)
41517          */
41518         "resized" : true
41519     };
41520     /** A collection of panels in this region. @type Roo.util.MixedCollection */
41521     this.panels = new Roo.util.MixedCollection();
41522     this.panels.getKey = this.getPanelId.createDelegate(this);
41523     this.box = null;
41524     this.activePanel = null;
41525     // ensure listeners are added...
41526     
41527     if (config.listeners || config.events) {
41528         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
41529             listeners : config.listeners || {},
41530             events : config.events || {}
41531         });
41532     }
41533     
41534     if(skipConfig !== true){
41535         this.applyConfig(config);
41536     }
41537 };
41538
41539 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
41540 {
41541     getPanelId : function(p){
41542         return p.getId();
41543     },
41544     
41545     applyConfig : function(config){
41546         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41547         this.config = config;
41548         
41549     },
41550     
41551     /**
41552      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
41553      * the width, for horizontal (north, south) the height.
41554      * @param {Number} newSize The new width or height
41555      */
41556     resizeTo : function(newSize){
41557         var el = this.el ? this.el :
41558                  (this.activePanel ? this.activePanel.getEl() : null);
41559         if(el){
41560             switch(this.position){
41561                 case "east":
41562                 case "west":
41563                     el.setWidth(newSize);
41564                     this.fireEvent("resized", this, newSize);
41565                 break;
41566                 case "north":
41567                 case "south":
41568                     el.setHeight(newSize);
41569                     this.fireEvent("resized", this, newSize);
41570                 break;                
41571             }
41572         }
41573     },
41574     
41575     getBox : function(){
41576         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
41577     },
41578     
41579     getMargins : function(){
41580         return this.margins;
41581     },
41582     
41583     updateBox : function(box){
41584         this.box = box;
41585         var el = this.activePanel.getEl();
41586         el.dom.style.left = box.x + "px";
41587         el.dom.style.top = box.y + "px";
41588         this.activePanel.setSize(box.width, box.height);
41589     },
41590     
41591     /**
41592      * Returns the container element for this region.
41593      * @return {Roo.Element}
41594      */
41595     getEl : function(){
41596         return this.activePanel;
41597     },
41598     
41599     /**
41600      * Returns true if this region is currently visible.
41601      * @return {Boolean}
41602      */
41603     isVisible : function(){
41604         return this.activePanel ? true : false;
41605     },
41606     
41607     setActivePanel : function(panel){
41608         panel = this.getPanel(panel);
41609         if(this.activePanel && this.activePanel != panel){
41610             this.activePanel.setActiveState(false);
41611             this.activePanel.getEl().setLeftTop(-10000,-10000);
41612         }
41613         this.activePanel = panel;
41614         panel.setActiveState(true);
41615         if(this.box){
41616             panel.setSize(this.box.width, this.box.height);
41617         }
41618         this.fireEvent("panelactivated", this, panel);
41619         this.fireEvent("invalidated");
41620     },
41621     
41622     /**
41623      * Show the specified panel.
41624      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
41625      * @return {Roo.ContentPanel} The shown panel or null
41626      */
41627     showPanel : function(panel){
41628         panel = this.getPanel(panel);
41629         if(panel){
41630             this.setActivePanel(panel);
41631         }
41632         return panel;
41633     },
41634     
41635     /**
41636      * Get the active panel for this region.
41637      * @return {Roo.ContentPanel} The active panel or null
41638      */
41639     getActivePanel : function(){
41640         return this.activePanel;
41641     },
41642     
41643     /**
41644      * Add the passed ContentPanel(s)
41645      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
41646      * @return {Roo.ContentPanel} The panel added (if only one was added)
41647      */
41648     add : function(panel){
41649         if(arguments.length > 1){
41650             for(var i = 0, len = arguments.length; i < len; i++) {
41651                 this.add(arguments[i]);
41652             }
41653             return null;
41654         }
41655         if(this.hasPanel(panel)){
41656             this.showPanel(panel);
41657             return panel;
41658         }
41659         var el = panel.getEl();
41660         if(el.dom.parentNode != this.mgr.el.dom){
41661             this.mgr.el.dom.appendChild(el.dom);
41662         }
41663         if(panel.setRegion){
41664             panel.setRegion(this);
41665         }
41666         this.panels.add(panel);
41667         el.setStyle("position", "absolute");
41668         if(!panel.background){
41669             this.setActivePanel(panel);
41670             if(this.config.initialSize && this.panels.getCount()==1){
41671                 this.resizeTo(this.config.initialSize);
41672             }
41673         }
41674         this.fireEvent("paneladded", this, panel);
41675         return panel;
41676     },
41677     
41678     /**
41679      * Returns true if the panel is in this region.
41680      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41681      * @return {Boolean}
41682      */
41683     hasPanel : function(panel){
41684         if(typeof panel == "object"){ // must be panel obj
41685             panel = panel.getId();
41686         }
41687         return this.getPanel(panel) ? true : false;
41688     },
41689     
41690     /**
41691      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
41692      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41693      * @param {Boolean} preservePanel Overrides the config preservePanel option
41694      * @return {Roo.ContentPanel} The panel that was removed
41695      */
41696     remove : function(panel, preservePanel){
41697         panel = this.getPanel(panel);
41698         if(!panel){
41699             return null;
41700         }
41701         var e = {};
41702         this.fireEvent("beforeremove", this, panel, e);
41703         if(e.cancel === true){
41704             return null;
41705         }
41706         var panelId = panel.getId();
41707         this.panels.removeKey(panelId);
41708         return panel;
41709     },
41710     
41711     /**
41712      * Returns the panel specified or null if it's not in this region.
41713      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41714      * @return {Roo.ContentPanel}
41715      */
41716     getPanel : function(id){
41717         if(typeof id == "object"){ // must be panel obj
41718             return id;
41719         }
41720         return this.panels.get(id);
41721     },
41722     
41723     /**
41724      * Returns this regions position (north/south/east/west/center).
41725      * @return {String} 
41726      */
41727     getPosition: function(){
41728         return this.position;    
41729     }
41730 });/*
41731  * Based on:
41732  * Ext JS Library 1.1.1
41733  * Copyright(c) 2006-2007, Ext JS, LLC.
41734  *
41735  * Originally Released Under LGPL - original licence link has changed is not relivant.
41736  *
41737  * Fork - LGPL
41738  * <script type="text/javascript">
41739  */
41740  
41741 /**
41742  * @class Roo.bootstrap.layout.Region
41743  * @extends Roo.bootstrap.layout.Basic
41744  * This class represents a region in a layout manager.
41745  
41746  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
41747  * @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})
41748  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
41749  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
41750  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
41751  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
41752  * @cfg {String}    title           The title for the region (overrides panel titles)
41753  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
41754  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
41755  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
41756  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
41757  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
41758  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
41759  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
41760  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
41761  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
41762  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
41763
41764  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
41765  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
41766  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
41767  * @cfg {Number}    width           For East/West panels
41768  * @cfg {Number}    height          For North/South panels
41769  * @cfg {Boolean}   split           To show the splitter
41770  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
41771  * 
41772  * @cfg {string}   cls             Extra CSS classes to add to region
41773  * 
41774  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41775  * @cfg {string}   region  the region that it inhabits..
41776  *
41777
41778  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
41779  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
41780
41781  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
41782  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
41783  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
41784  */
41785 Roo.bootstrap.layout.Region = function(config)
41786 {
41787     this.applyConfig(config);
41788
41789     var mgr = config.mgr;
41790     var pos = config.region;
41791     config.skipConfig = true;
41792     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
41793     
41794     if (mgr.el) {
41795         this.onRender(mgr.el);   
41796     }
41797      
41798     this.visible = true;
41799     this.collapsed = false;
41800     this.unrendered_panels = [];
41801 };
41802
41803 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
41804
41805     position: '', // set by wrapper (eg. north/south etc..)
41806     unrendered_panels : null,  // unrendered panels.
41807     
41808     tabPosition : false,
41809     
41810     mgr: false, // points to 'Border'
41811     
41812     
41813     createBody : function(){
41814         /** This region's body element 
41815         * @type Roo.Element */
41816         this.bodyEl = this.el.createChild({
41817                 tag: "div",
41818                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
41819         });
41820     },
41821
41822     onRender: function(ctr, pos)
41823     {
41824         var dh = Roo.DomHelper;
41825         /** This region's container element 
41826         * @type Roo.Element */
41827         this.el = dh.append(ctr.dom, {
41828                 tag: "div",
41829                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
41830             }, true);
41831         /** This region's title element 
41832         * @type Roo.Element */
41833     
41834         this.titleEl = dh.append(this.el.dom,  {
41835                 tag: "div",
41836                 unselectable: "on",
41837                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
41838                 children:[
41839                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
41840                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
41841                 ]
41842             }, true);
41843         
41844         this.titleEl.enableDisplayMode();
41845         /** This region's title text element 
41846         * @type HTMLElement */
41847         this.titleTextEl = this.titleEl.dom.firstChild;
41848         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
41849         /*
41850         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
41851         this.closeBtn.enableDisplayMode();
41852         this.closeBtn.on("click", this.closeClicked, this);
41853         this.closeBtn.hide();
41854     */
41855         this.createBody(this.config);
41856         if(this.config.hideWhenEmpty){
41857             this.hide();
41858             this.on("paneladded", this.validateVisibility, this);
41859             this.on("panelremoved", this.validateVisibility, this);
41860         }
41861         if(this.autoScroll){
41862             this.bodyEl.setStyle("overflow", "auto");
41863         }else{
41864             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
41865         }
41866         //if(c.titlebar !== false){
41867             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
41868                 this.titleEl.hide();
41869             }else{
41870                 this.titleEl.show();
41871                 if(this.config.title){
41872                     this.titleTextEl.innerHTML = this.config.title;
41873                 }
41874             }
41875         //}
41876         if(this.config.collapsed){
41877             this.collapse(true);
41878         }
41879         if(this.config.hidden){
41880             this.hide();
41881         }
41882         
41883         if (this.unrendered_panels && this.unrendered_panels.length) {
41884             for (var i =0;i< this.unrendered_panels.length; i++) {
41885                 this.add(this.unrendered_panels[i]);
41886             }
41887             this.unrendered_panels = null;
41888             
41889         }
41890         
41891     },
41892     
41893     applyConfig : function(c)
41894     {
41895         /*
41896          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
41897             var dh = Roo.DomHelper;
41898             if(c.titlebar !== false){
41899                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
41900                 this.collapseBtn.on("click", this.collapse, this);
41901                 this.collapseBtn.enableDisplayMode();
41902                 /*
41903                 if(c.showPin === true || this.showPin){
41904                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
41905                     this.stickBtn.enableDisplayMode();
41906                     this.stickBtn.on("click", this.expand, this);
41907                     this.stickBtn.hide();
41908                 }
41909                 
41910             }
41911             */
41912             /** This region's collapsed element
41913             * @type Roo.Element */
41914             /*
41915              *
41916             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
41917                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
41918             ]}, true);
41919             
41920             if(c.floatable !== false){
41921                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
41922                this.collapsedEl.on("click", this.collapseClick, this);
41923             }
41924
41925             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
41926                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
41927                    id: "message", unselectable: "on", style:{"float":"left"}});
41928                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
41929              }
41930             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
41931             this.expandBtn.on("click", this.expand, this);
41932             
41933         }
41934         
41935         if(this.collapseBtn){
41936             this.collapseBtn.setVisible(c.collapsible == true);
41937         }
41938         
41939         this.cmargins = c.cmargins || this.cmargins ||
41940                          (this.position == "west" || this.position == "east" ?
41941                              {top: 0, left: 2, right:2, bottom: 0} :
41942                              {top: 2, left: 0, right:0, bottom: 2});
41943         */
41944         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41945         
41946         
41947         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
41948         
41949         this.autoScroll = c.autoScroll || false;
41950         
41951         
41952        
41953         
41954         this.duration = c.duration || .30;
41955         this.slideDuration = c.slideDuration || .45;
41956         this.config = c;
41957        
41958     },
41959     /**
41960      * Returns true if this region is currently visible.
41961      * @return {Boolean}
41962      */
41963     isVisible : function(){
41964         return this.visible;
41965     },
41966
41967     /**
41968      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
41969      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
41970      */
41971     //setCollapsedTitle : function(title){
41972     //    title = title || "&#160;";
41973      //   if(this.collapsedTitleTextEl){
41974       //      this.collapsedTitleTextEl.innerHTML = title;
41975        // }
41976     //},
41977
41978     getBox : function(){
41979         var b;
41980       //  if(!this.collapsed){
41981             b = this.el.getBox(false, true);
41982        // }else{
41983           //  b = this.collapsedEl.getBox(false, true);
41984         //}
41985         return b;
41986     },
41987
41988     getMargins : function(){
41989         return this.margins;
41990         //return this.collapsed ? this.cmargins : this.margins;
41991     },
41992 /*
41993     highlight : function(){
41994         this.el.addClass("x-layout-panel-dragover");
41995     },
41996
41997     unhighlight : function(){
41998         this.el.removeClass("x-layout-panel-dragover");
41999     },
42000 */
42001     updateBox : function(box)
42002     {
42003         if (!this.bodyEl) {
42004             return; // not rendered yet..
42005         }
42006         
42007         this.box = box;
42008         if(!this.collapsed){
42009             this.el.dom.style.left = box.x + "px";
42010             this.el.dom.style.top = box.y + "px";
42011             this.updateBody(box.width, box.height);
42012         }else{
42013             this.collapsedEl.dom.style.left = box.x + "px";
42014             this.collapsedEl.dom.style.top = box.y + "px";
42015             this.collapsedEl.setSize(box.width, box.height);
42016         }
42017         if(this.tabs){
42018             this.tabs.autoSizeTabs();
42019         }
42020     },
42021
42022     updateBody : function(w, h)
42023     {
42024         if(w !== null){
42025             this.el.setWidth(w);
42026             w -= this.el.getBorderWidth("rl");
42027             if(this.config.adjustments){
42028                 w += this.config.adjustments[0];
42029             }
42030         }
42031         if(h !== null && h > 0){
42032             this.el.setHeight(h);
42033             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
42034             h -= this.el.getBorderWidth("tb");
42035             if(this.config.adjustments){
42036                 h += this.config.adjustments[1];
42037             }
42038             this.bodyEl.setHeight(h);
42039             if(this.tabs){
42040                 h = this.tabs.syncHeight(h);
42041             }
42042         }
42043         if(this.panelSize){
42044             w = w !== null ? w : this.panelSize.width;
42045             h = h !== null ? h : this.panelSize.height;
42046         }
42047         if(this.activePanel){
42048             var el = this.activePanel.getEl();
42049             w = w !== null ? w : el.getWidth();
42050             h = h !== null ? h : el.getHeight();
42051             this.panelSize = {width: w, height: h};
42052             this.activePanel.setSize(w, h);
42053         }
42054         if(Roo.isIE && this.tabs){
42055             this.tabs.el.repaint();
42056         }
42057     },
42058
42059     /**
42060      * Returns the container element for this region.
42061      * @return {Roo.Element}
42062      */
42063     getEl : function(){
42064         return this.el;
42065     },
42066
42067     /**
42068      * Hides this region.
42069      */
42070     hide : function(){
42071         //if(!this.collapsed){
42072             this.el.dom.style.left = "-2000px";
42073             this.el.hide();
42074         //}else{
42075          //   this.collapsedEl.dom.style.left = "-2000px";
42076          //   this.collapsedEl.hide();
42077        // }
42078         this.visible = false;
42079         this.fireEvent("visibilitychange", this, false);
42080     },
42081
42082     /**
42083      * Shows this region if it was previously hidden.
42084      */
42085     show : function(){
42086         //if(!this.collapsed){
42087             this.el.show();
42088         //}else{
42089         //    this.collapsedEl.show();
42090        // }
42091         this.visible = true;
42092         this.fireEvent("visibilitychange", this, true);
42093     },
42094 /*
42095     closeClicked : function(){
42096         if(this.activePanel){
42097             this.remove(this.activePanel);
42098         }
42099     },
42100
42101     collapseClick : function(e){
42102         if(this.isSlid){
42103            e.stopPropagation();
42104            this.slideIn();
42105         }else{
42106            e.stopPropagation();
42107            this.slideOut();
42108         }
42109     },
42110 */
42111     /**
42112      * Collapses this region.
42113      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
42114      */
42115     /*
42116     collapse : function(skipAnim, skipCheck = false){
42117         if(this.collapsed) {
42118             return;
42119         }
42120         
42121         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
42122             
42123             this.collapsed = true;
42124             if(this.split){
42125                 this.split.el.hide();
42126             }
42127             if(this.config.animate && skipAnim !== true){
42128                 this.fireEvent("invalidated", this);
42129                 this.animateCollapse();
42130             }else{
42131                 this.el.setLocation(-20000,-20000);
42132                 this.el.hide();
42133                 this.collapsedEl.show();
42134                 this.fireEvent("collapsed", this);
42135                 this.fireEvent("invalidated", this);
42136             }
42137         }
42138         
42139     },
42140 */
42141     animateCollapse : function(){
42142         // overridden
42143     },
42144
42145     /**
42146      * Expands this region if it was previously collapsed.
42147      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
42148      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
42149      */
42150     /*
42151     expand : function(e, skipAnim){
42152         if(e) {
42153             e.stopPropagation();
42154         }
42155         if(!this.collapsed || this.el.hasActiveFx()) {
42156             return;
42157         }
42158         if(this.isSlid){
42159             this.afterSlideIn();
42160             skipAnim = true;
42161         }
42162         this.collapsed = false;
42163         if(this.config.animate && skipAnim !== true){
42164             this.animateExpand();
42165         }else{
42166             this.el.show();
42167             if(this.split){
42168                 this.split.el.show();
42169             }
42170             this.collapsedEl.setLocation(-2000,-2000);
42171             this.collapsedEl.hide();
42172             this.fireEvent("invalidated", this);
42173             this.fireEvent("expanded", this);
42174         }
42175     },
42176 */
42177     animateExpand : function(){
42178         // overridden
42179     },
42180
42181     initTabs : function()
42182     {
42183         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
42184         
42185         var ts = new Roo.bootstrap.panel.Tabs({
42186             el: this.bodyEl.dom,
42187             region : this,
42188             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
42189             disableTooltips: this.config.disableTabTips,
42190             toolbar : this.config.toolbar
42191         });
42192         
42193         if(this.config.hideTabs){
42194             ts.stripWrap.setDisplayed(false);
42195         }
42196         this.tabs = ts;
42197         ts.resizeTabs = this.config.resizeTabs === true;
42198         ts.minTabWidth = this.config.minTabWidth || 40;
42199         ts.maxTabWidth = this.config.maxTabWidth || 250;
42200         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
42201         ts.monitorResize = false;
42202         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
42203         ts.bodyEl.addClass('roo-layout-tabs-body');
42204         this.panels.each(this.initPanelAsTab, this);
42205     },
42206
42207     initPanelAsTab : function(panel){
42208         var ti = this.tabs.addTab(
42209             panel.getEl().id,
42210             panel.getTitle(),
42211             null,
42212             this.config.closeOnTab && panel.isClosable(),
42213             panel.tpl
42214         );
42215         if(panel.tabTip !== undefined){
42216             ti.setTooltip(panel.tabTip);
42217         }
42218         ti.on("activate", function(){
42219               this.setActivePanel(panel);
42220         }, this);
42221         
42222         if(this.config.closeOnTab){
42223             ti.on("beforeclose", function(t, e){
42224                 e.cancel = true;
42225                 this.remove(panel);
42226             }, this);
42227         }
42228         
42229         panel.tabItem = ti;
42230         
42231         return ti;
42232     },
42233
42234     updatePanelTitle : function(panel, title)
42235     {
42236         if(this.activePanel == panel){
42237             this.updateTitle(title);
42238         }
42239         if(this.tabs){
42240             var ti = this.tabs.getTab(panel.getEl().id);
42241             ti.setText(title);
42242             if(panel.tabTip !== undefined){
42243                 ti.setTooltip(panel.tabTip);
42244             }
42245         }
42246     },
42247
42248     updateTitle : function(title){
42249         if(this.titleTextEl && !this.config.title){
42250             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
42251         }
42252     },
42253
42254     setActivePanel : function(panel)
42255     {
42256         panel = this.getPanel(panel);
42257         if(this.activePanel && this.activePanel != panel){
42258             if(this.activePanel.setActiveState(false) === false){
42259                 return;
42260             }
42261         }
42262         this.activePanel = panel;
42263         panel.setActiveState(true);
42264         if(this.panelSize){
42265             panel.setSize(this.panelSize.width, this.panelSize.height);
42266         }
42267         if(this.closeBtn){
42268             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
42269         }
42270         this.updateTitle(panel.getTitle());
42271         if(this.tabs){
42272             this.fireEvent("invalidated", this);
42273         }
42274         this.fireEvent("panelactivated", this, panel);
42275     },
42276
42277     /**
42278      * Shows the specified panel.
42279      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
42280      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
42281      */
42282     showPanel : function(panel)
42283     {
42284         panel = this.getPanel(panel);
42285         if(panel){
42286             if(this.tabs){
42287                 var tab = this.tabs.getTab(panel.getEl().id);
42288                 if(tab.isHidden()){
42289                     this.tabs.unhideTab(tab.id);
42290                 }
42291                 tab.activate();
42292             }else{
42293                 this.setActivePanel(panel);
42294             }
42295         }
42296         return panel;
42297     },
42298
42299     /**
42300      * Get the active panel for this region.
42301      * @return {Roo.ContentPanel} The active panel or null
42302      */
42303     getActivePanel : function(){
42304         return this.activePanel;
42305     },
42306
42307     validateVisibility : function(){
42308         if(this.panels.getCount() < 1){
42309             this.updateTitle("&#160;");
42310             this.closeBtn.hide();
42311             this.hide();
42312         }else{
42313             if(!this.isVisible()){
42314                 this.show();
42315             }
42316         }
42317     },
42318
42319     /**
42320      * Adds the passed ContentPanel(s) to this region.
42321      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42322      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
42323      */
42324     add : function(panel)
42325     {
42326         if(arguments.length > 1){
42327             for(var i = 0, len = arguments.length; i < len; i++) {
42328                 this.add(arguments[i]);
42329             }
42330             return null;
42331         }
42332         
42333         // if we have not been rendered yet, then we can not really do much of this..
42334         if (!this.bodyEl) {
42335             this.unrendered_panels.push(panel);
42336             return panel;
42337         }
42338         
42339         
42340         
42341         
42342         if(this.hasPanel(panel)){
42343             this.showPanel(panel);
42344             return panel;
42345         }
42346         panel.setRegion(this);
42347         this.panels.add(panel);
42348        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
42349             // sinle panel - no tab...?? would it not be better to render it with the tabs,
42350             // and hide them... ???
42351             this.bodyEl.dom.appendChild(panel.getEl().dom);
42352             if(panel.background !== true){
42353                 this.setActivePanel(panel);
42354             }
42355             this.fireEvent("paneladded", this, panel);
42356             return panel;
42357         }
42358         */
42359         if(!this.tabs){
42360             this.initTabs();
42361         }else{
42362             this.initPanelAsTab(panel);
42363         }
42364         
42365         
42366         if(panel.background !== true){
42367             this.tabs.activate(panel.getEl().id);
42368         }
42369         this.fireEvent("paneladded", this, panel);
42370         return panel;
42371     },
42372
42373     /**
42374      * Hides the tab for the specified panel.
42375      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42376      */
42377     hidePanel : function(panel){
42378         if(this.tabs && (panel = this.getPanel(panel))){
42379             this.tabs.hideTab(panel.getEl().id);
42380         }
42381     },
42382
42383     /**
42384      * Unhides the tab for a previously hidden panel.
42385      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42386      */
42387     unhidePanel : function(panel){
42388         if(this.tabs && (panel = this.getPanel(panel))){
42389             this.tabs.unhideTab(panel.getEl().id);
42390         }
42391     },
42392
42393     clearPanels : function(){
42394         while(this.panels.getCount() > 0){
42395              this.remove(this.panels.first());
42396         }
42397     },
42398
42399     /**
42400      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42401      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42402      * @param {Boolean} preservePanel Overrides the config preservePanel option
42403      * @return {Roo.ContentPanel} The panel that was removed
42404      */
42405     remove : function(panel, preservePanel)
42406     {
42407         panel = this.getPanel(panel);
42408         if(!panel){
42409             return null;
42410         }
42411         var e = {};
42412         this.fireEvent("beforeremove", this, panel, e);
42413         if(e.cancel === true){
42414             return null;
42415         }
42416         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
42417         var panelId = panel.getId();
42418         this.panels.removeKey(panelId);
42419         if(preservePanel){
42420             document.body.appendChild(panel.getEl().dom);
42421         }
42422         if(this.tabs){
42423             this.tabs.removeTab(panel.getEl().id);
42424         }else if (!preservePanel){
42425             this.bodyEl.dom.removeChild(panel.getEl().dom);
42426         }
42427         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
42428             var p = this.panels.first();
42429             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
42430             tempEl.appendChild(p.getEl().dom);
42431             this.bodyEl.update("");
42432             this.bodyEl.dom.appendChild(p.getEl().dom);
42433             tempEl = null;
42434             this.updateTitle(p.getTitle());
42435             this.tabs = null;
42436             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
42437             this.setActivePanel(p);
42438         }
42439         panel.setRegion(null);
42440         if(this.activePanel == panel){
42441             this.activePanel = null;
42442         }
42443         if(this.config.autoDestroy !== false && preservePanel !== true){
42444             try{panel.destroy();}catch(e){}
42445         }
42446         this.fireEvent("panelremoved", this, panel);
42447         return panel;
42448     },
42449
42450     /**
42451      * Returns the TabPanel component used by this region
42452      * @return {Roo.TabPanel}
42453      */
42454     getTabs : function(){
42455         return this.tabs;
42456     },
42457
42458     createTool : function(parentEl, className){
42459         var btn = Roo.DomHelper.append(parentEl, {
42460             tag: "div",
42461             cls: "x-layout-tools-button",
42462             children: [ {
42463                 tag: "div",
42464                 cls: "roo-layout-tools-button-inner " + className,
42465                 html: "&#160;"
42466             }]
42467         }, true);
42468         btn.addClassOnOver("roo-layout-tools-button-over");
42469         return btn;
42470     }
42471 });/*
42472  * Based on:
42473  * Ext JS Library 1.1.1
42474  * Copyright(c) 2006-2007, Ext JS, LLC.
42475  *
42476  * Originally Released Under LGPL - original licence link has changed is not relivant.
42477  *
42478  * Fork - LGPL
42479  * <script type="text/javascript">
42480  */
42481  
42482
42483
42484 /**
42485  * @class Roo.SplitLayoutRegion
42486  * @extends Roo.LayoutRegion
42487  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
42488  */
42489 Roo.bootstrap.layout.Split = function(config){
42490     this.cursor = config.cursor;
42491     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
42492 };
42493
42494 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
42495 {
42496     splitTip : "Drag to resize.",
42497     collapsibleSplitTip : "Drag to resize. Double click to hide.",
42498     useSplitTips : false,
42499
42500     applyConfig : function(config){
42501         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
42502     },
42503     
42504     onRender : function(ctr,pos) {
42505         
42506         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
42507         if(!this.config.split){
42508             return;
42509         }
42510         if(!this.split){
42511             
42512             var splitEl = Roo.DomHelper.append(ctr.dom,  {
42513                             tag: "div",
42514                             id: this.el.id + "-split",
42515                             cls: "roo-layout-split roo-layout-split-"+this.position,
42516                             html: "&#160;"
42517             });
42518             /** The SplitBar for this region 
42519             * @type Roo.SplitBar */
42520             // does not exist yet...
42521             Roo.log([this.position, this.orientation]);
42522             
42523             this.split = new Roo.bootstrap.SplitBar({
42524                 dragElement : splitEl,
42525                 resizingElement: this.el,
42526                 orientation : this.orientation
42527             });
42528             
42529             this.split.on("moved", this.onSplitMove, this);
42530             this.split.useShim = this.config.useShim === true;
42531             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
42532             if(this.useSplitTips){
42533                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
42534             }
42535             //if(config.collapsible){
42536             //    this.split.el.on("dblclick", this.collapse,  this);
42537             //}
42538         }
42539         if(typeof this.config.minSize != "undefined"){
42540             this.split.minSize = this.config.minSize;
42541         }
42542         if(typeof this.config.maxSize != "undefined"){
42543             this.split.maxSize = this.config.maxSize;
42544         }
42545         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
42546             this.hideSplitter();
42547         }
42548         
42549     },
42550
42551     getHMaxSize : function(){
42552          var cmax = this.config.maxSize || 10000;
42553          var center = this.mgr.getRegion("center");
42554          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
42555     },
42556
42557     getVMaxSize : function(){
42558          var cmax = this.config.maxSize || 10000;
42559          var center = this.mgr.getRegion("center");
42560          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
42561     },
42562
42563     onSplitMove : function(split, newSize){
42564         this.fireEvent("resized", this, newSize);
42565     },
42566     
42567     /** 
42568      * Returns the {@link Roo.SplitBar} for this region.
42569      * @return {Roo.SplitBar}
42570      */
42571     getSplitBar : function(){
42572         return this.split;
42573     },
42574     
42575     hide : function(){
42576         this.hideSplitter();
42577         Roo.bootstrap.layout.Split.superclass.hide.call(this);
42578     },
42579
42580     hideSplitter : function(){
42581         if(this.split){
42582             this.split.el.setLocation(-2000,-2000);
42583             this.split.el.hide();
42584         }
42585     },
42586
42587     show : function(){
42588         if(this.split){
42589             this.split.el.show();
42590         }
42591         Roo.bootstrap.layout.Split.superclass.show.call(this);
42592     },
42593     
42594     beforeSlide: function(){
42595         if(Roo.isGecko){// firefox overflow auto bug workaround
42596             this.bodyEl.clip();
42597             if(this.tabs) {
42598                 this.tabs.bodyEl.clip();
42599             }
42600             if(this.activePanel){
42601                 this.activePanel.getEl().clip();
42602                 
42603                 if(this.activePanel.beforeSlide){
42604                     this.activePanel.beforeSlide();
42605                 }
42606             }
42607         }
42608     },
42609     
42610     afterSlide : function(){
42611         if(Roo.isGecko){// firefox overflow auto bug workaround
42612             this.bodyEl.unclip();
42613             if(this.tabs) {
42614                 this.tabs.bodyEl.unclip();
42615             }
42616             if(this.activePanel){
42617                 this.activePanel.getEl().unclip();
42618                 if(this.activePanel.afterSlide){
42619                     this.activePanel.afterSlide();
42620                 }
42621             }
42622         }
42623     },
42624
42625     initAutoHide : function(){
42626         if(this.autoHide !== false){
42627             if(!this.autoHideHd){
42628                 var st = new Roo.util.DelayedTask(this.slideIn, this);
42629                 this.autoHideHd = {
42630                     "mouseout": function(e){
42631                         if(!e.within(this.el, true)){
42632                             st.delay(500);
42633                         }
42634                     },
42635                     "mouseover" : function(e){
42636                         st.cancel();
42637                     },
42638                     scope : this
42639                 };
42640             }
42641             this.el.on(this.autoHideHd);
42642         }
42643     },
42644
42645     clearAutoHide : function(){
42646         if(this.autoHide !== false){
42647             this.el.un("mouseout", this.autoHideHd.mouseout);
42648             this.el.un("mouseover", this.autoHideHd.mouseover);
42649         }
42650     },
42651
42652     clearMonitor : function(){
42653         Roo.get(document).un("click", this.slideInIf, this);
42654     },
42655
42656     // these names are backwards but not changed for compat
42657     slideOut : function(){
42658         if(this.isSlid || this.el.hasActiveFx()){
42659             return;
42660         }
42661         this.isSlid = true;
42662         if(this.collapseBtn){
42663             this.collapseBtn.hide();
42664         }
42665         this.closeBtnState = this.closeBtn.getStyle('display');
42666         this.closeBtn.hide();
42667         if(this.stickBtn){
42668             this.stickBtn.show();
42669         }
42670         this.el.show();
42671         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
42672         this.beforeSlide();
42673         this.el.setStyle("z-index", 10001);
42674         this.el.slideIn(this.getSlideAnchor(), {
42675             callback: function(){
42676                 this.afterSlide();
42677                 this.initAutoHide();
42678                 Roo.get(document).on("click", this.slideInIf, this);
42679                 this.fireEvent("slideshow", this);
42680             },
42681             scope: this,
42682             block: true
42683         });
42684     },
42685
42686     afterSlideIn : function(){
42687         this.clearAutoHide();
42688         this.isSlid = false;
42689         this.clearMonitor();
42690         this.el.setStyle("z-index", "");
42691         if(this.collapseBtn){
42692             this.collapseBtn.show();
42693         }
42694         this.closeBtn.setStyle('display', this.closeBtnState);
42695         if(this.stickBtn){
42696             this.stickBtn.hide();
42697         }
42698         this.fireEvent("slidehide", this);
42699     },
42700
42701     slideIn : function(cb){
42702         if(!this.isSlid || this.el.hasActiveFx()){
42703             Roo.callback(cb);
42704             return;
42705         }
42706         this.isSlid = false;
42707         this.beforeSlide();
42708         this.el.slideOut(this.getSlideAnchor(), {
42709             callback: function(){
42710                 this.el.setLeftTop(-10000, -10000);
42711                 this.afterSlide();
42712                 this.afterSlideIn();
42713                 Roo.callback(cb);
42714             },
42715             scope: this,
42716             block: true
42717         });
42718     },
42719     
42720     slideInIf : function(e){
42721         if(!e.within(this.el)){
42722             this.slideIn();
42723         }
42724     },
42725
42726     animateCollapse : function(){
42727         this.beforeSlide();
42728         this.el.setStyle("z-index", 20000);
42729         var anchor = this.getSlideAnchor();
42730         this.el.slideOut(anchor, {
42731             callback : function(){
42732                 this.el.setStyle("z-index", "");
42733                 this.collapsedEl.slideIn(anchor, {duration:.3});
42734                 this.afterSlide();
42735                 this.el.setLocation(-10000,-10000);
42736                 this.el.hide();
42737                 this.fireEvent("collapsed", this);
42738             },
42739             scope: this,
42740             block: true
42741         });
42742     },
42743
42744     animateExpand : function(){
42745         this.beforeSlide();
42746         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
42747         this.el.setStyle("z-index", 20000);
42748         this.collapsedEl.hide({
42749             duration:.1
42750         });
42751         this.el.slideIn(this.getSlideAnchor(), {
42752             callback : function(){
42753                 this.el.setStyle("z-index", "");
42754                 this.afterSlide();
42755                 if(this.split){
42756                     this.split.el.show();
42757                 }
42758                 this.fireEvent("invalidated", this);
42759                 this.fireEvent("expanded", this);
42760             },
42761             scope: this,
42762             block: true
42763         });
42764     },
42765
42766     anchors : {
42767         "west" : "left",
42768         "east" : "right",
42769         "north" : "top",
42770         "south" : "bottom"
42771     },
42772
42773     sanchors : {
42774         "west" : "l",
42775         "east" : "r",
42776         "north" : "t",
42777         "south" : "b"
42778     },
42779
42780     canchors : {
42781         "west" : "tl-tr",
42782         "east" : "tr-tl",
42783         "north" : "tl-bl",
42784         "south" : "bl-tl"
42785     },
42786
42787     getAnchor : function(){
42788         return this.anchors[this.position];
42789     },
42790
42791     getCollapseAnchor : function(){
42792         return this.canchors[this.position];
42793     },
42794
42795     getSlideAnchor : function(){
42796         return this.sanchors[this.position];
42797     },
42798
42799     getAlignAdj : function(){
42800         var cm = this.cmargins;
42801         switch(this.position){
42802             case "west":
42803                 return [0, 0];
42804             break;
42805             case "east":
42806                 return [0, 0];
42807             break;
42808             case "north":
42809                 return [0, 0];
42810             break;
42811             case "south":
42812                 return [0, 0];
42813             break;
42814         }
42815     },
42816
42817     getExpandAdj : function(){
42818         var c = this.collapsedEl, cm = this.cmargins;
42819         switch(this.position){
42820             case "west":
42821                 return [-(cm.right+c.getWidth()+cm.left), 0];
42822             break;
42823             case "east":
42824                 return [cm.right+c.getWidth()+cm.left, 0];
42825             break;
42826             case "north":
42827                 return [0, -(cm.top+cm.bottom+c.getHeight())];
42828             break;
42829             case "south":
42830                 return [0, cm.top+cm.bottom+c.getHeight()];
42831             break;
42832         }
42833     }
42834 });/*
42835  * Based on:
42836  * Ext JS Library 1.1.1
42837  * Copyright(c) 2006-2007, Ext JS, LLC.
42838  *
42839  * Originally Released Under LGPL - original licence link has changed is not relivant.
42840  *
42841  * Fork - LGPL
42842  * <script type="text/javascript">
42843  */
42844 /*
42845  * These classes are private internal classes
42846  */
42847 Roo.bootstrap.layout.Center = function(config){
42848     config.region = "center";
42849     Roo.bootstrap.layout.Region.call(this, config);
42850     this.visible = true;
42851     this.minWidth = config.minWidth || 20;
42852     this.minHeight = config.minHeight || 20;
42853 };
42854
42855 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
42856     hide : function(){
42857         // center panel can't be hidden
42858     },
42859     
42860     show : function(){
42861         // center panel can't be hidden
42862     },
42863     
42864     getMinWidth: function(){
42865         return this.minWidth;
42866     },
42867     
42868     getMinHeight: function(){
42869         return this.minHeight;
42870     }
42871 });
42872
42873
42874
42875
42876  
42877
42878
42879
42880
42881
42882
42883 Roo.bootstrap.layout.North = function(config)
42884 {
42885     config.region = 'north';
42886     config.cursor = 'n-resize';
42887     
42888     Roo.bootstrap.layout.Split.call(this, config);
42889     
42890     
42891     if(this.split){
42892         this.split.placement = Roo.bootstrap.SplitBar.TOP;
42893         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42894         this.split.el.addClass("roo-layout-split-v");
42895     }
42896     //var size = config.initialSize || config.height;
42897     //if(this.el && typeof size != "undefined"){
42898     //    this.el.setHeight(size);
42899     //}
42900 };
42901 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
42902 {
42903     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42904      
42905      
42906     onRender : function(ctr, pos)
42907     {
42908         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42909         var size = this.config.initialSize || this.config.height;
42910         if(this.el && typeof size != "undefined"){
42911             this.el.setHeight(size);
42912         }
42913     
42914     },
42915     
42916     getBox : function(){
42917         if(this.collapsed){
42918             return this.collapsedEl.getBox();
42919         }
42920         var box = this.el.getBox();
42921         if(this.split){
42922             box.height += this.split.el.getHeight();
42923         }
42924         return box;
42925     },
42926     
42927     updateBox : function(box){
42928         if(this.split && !this.collapsed){
42929             box.height -= this.split.el.getHeight();
42930             this.split.el.setLeft(box.x);
42931             this.split.el.setTop(box.y+box.height);
42932             this.split.el.setWidth(box.width);
42933         }
42934         if(this.collapsed){
42935             this.updateBody(box.width, null);
42936         }
42937         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42938     }
42939 });
42940
42941
42942
42943
42944
42945 Roo.bootstrap.layout.South = function(config){
42946     config.region = 'south';
42947     config.cursor = 's-resize';
42948     Roo.bootstrap.layout.Split.call(this, config);
42949     if(this.split){
42950         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
42951         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42952         this.split.el.addClass("roo-layout-split-v");
42953     }
42954     
42955 };
42956
42957 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
42958     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42959     
42960     onRender : function(ctr, pos)
42961     {
42962         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42963         var size = this.config.initialSize || this.config.height;
42964         if(this.el && typeof size != "undefined"){
42965             this.el.setHeight(size);
42966         }
42967     
42968     },
42969     
42970     getBox : function(){
42971         if(this.collapsed){
42972             return this.collapsedEl.getBox();
42973         }
42974         var box = this.el.getBox();
42975         if(this.split){
42976             var sh = this.split.el.getHeight();
42977             box.height += sh;
42978             box.y -= sh;
42979         }
42980         return box;
42981     },
42982     
42983     updateBox : function(box){
42984         if(this.split && !this.collapsed){
42985             var sh = this.split.el.getHeight();
42986             box.height -= sh;
42987             box.y += sh;
42988             this.split.el.setLeft(box.x);
42989             this.split.el.setTop(box.y-sh);
42990             this.split.el.setWidth(box.width);
42991         }
42992         if(this.collapsed){
42993             this.updateBody(box.width, null);
42994         }
42995         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42996     }
42997 });
42998
42999 Roo.bootstrap.layout.East = function(config){
43000     config.region = "east";
43001     config.cursor = "e-resize";
43002     Roo.bootstrap.layout.Split.call(this, config);
43003     if(this.split){
43004         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
43005         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
43006         this.split.el.addClass("roo-layout-split-h");
43007     }
43008     
43009 };
43010 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
43011     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
43012     
43013     onRender : function(ctr, pos)
43014     {
43015         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
43016         var size = this.config.initialSize || this.config.width;
43017         if(this.el && typeof size != "undefined"){
43018             this.el.setWidth(size);
43019         }
43020     
43021     },
43022     
43023     getBox : function(){
43024         if(this.collapsed){
43025             return this.collapsedEl.getBox();
43026         }
43027         var box = this.el.getBox();
43028         if(this.split){
43029             var sw = this.split.el.getWidth();
43030             box.width += sw;
43031             box.x -= sw;
43032         }
43033         return box;
43034     },
43035
43036     updateBox : function(box){
43037         if(this.split && !this.collapsed){
43038             var sw = this.split.el.getWidth();
43039             box.width -= sw;
43040             this.split.el.setLeft(box.x);
43041             this.split.el.setTop(box.y);
43042             this.split.el.setHeight(box.height);
43043             box.x += sw;
43044         }
43045         if(this.collapsed){
43046             this.updateBody(null, box.height);
43047         }
43048         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
43049     }
43050 });
43051
43052 Roo.bootstrap.layout.West = function(config){
43053     config.region = "west";
43054     config.cursor = "w-resize";
43055     
43056     Roo.bootstrap.layout.Split.call(this, config);
43057     if(this.split){
43058         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
43059         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
43060         this.split.el.addClass("roo-layout-split-h");
43061     }
43062     
43063 };
43064 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
43065     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
43066     
43067     onRender: function(ctr, pos)
43068     {
43069         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
43070         var size = this.config.initialSize || this.config.width;
43071         if(typeof size != "undefined"){
43072             this.el.setWidth(size);
43073         }
43074     },
43075     
43076     getBox : function(){
43077         if(this.collapsed){
43078             return this.collapsedEl.getBox();
43079         }
43080         var box = this.el.getBox();
43081         if (box.width == 0) {
43082             box.width = this.config.width; // kludge?
43083         }
43084         if(this.split){
43085             box.width += this.split.el.getWidth();
43086         }
43087         return box;
43088     },
43089     
43090     updateBox : function(box){
43091         if(this.split && !this.collapsed){
43092             var sw = this.split.el.getWidth();
43093             box.width -= sw;
43094             this.split.el.setLeft(box.x+box.width);
43095             this.split.el.setTop(box.y);
43096             this.split.el.setHeight(box.height);
43097         }
43098         if(this.collapsed){
43099             this.updateBody(null, box.height);
43100         }
43101         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
43102     }
43103 });/*
43104  * Based on:
43105  * Ext JS Library 1.1.1
43106  * Copyright(c) 2006-2007, Ext JS, LLC.
43107  *
43108  * Originally Released Under LGPL - original licence link has changed is not relivant.
43109  *
43110  * Fork - LGPL
43111  * <script type="text/javascript">
43112  */
43113 /**
43114  * @class Roo.bootstrap.paenl.Content
43115  * @extends Roo.util.Observable
43116  * @children Roo.bootstrap.Component
43117  * @parent builder Roo.bootstrap.layout.Border
43118  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
43119  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
43120  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
43121  * @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
43122  * @cfg {Boolean}   closable      True if the panel can be closed/removed
43123  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
43124  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
43125  * @cfg {Toolbar}   toolbar       A toolbar for this panel
43126  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
43127  * @cfg {String} title          The title for this panel
43128  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
43129  * @cfg {String} url            Calls {@link #setUrl} with this value
43130  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
43131  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
43132  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
43133  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
43134  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
43135  * @cfg {Boolean} badges render the badges
43136  * @cfg {String} cls  extra classes to use  
43137  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
43138  
43139  * @constructor
43140  * Create a new ContentPanel.
43141  * @param {String/Object} config A string to set only the title or a config object
43142  
43143  */
43144 Roo.bootstrap.panel.Content = function( config){
43145     
43146     this.tpl = config.tpl || false;
43147     
43148     var el = config.el;
43149     var content = config.content;
43150
43151     if(config.autoCreate){ // xtype is available if this is called from factory
43152         el = Roo.id();
43153     }
43154     this.el = Roo.get(el);
43155     if(!this.el && config && config.autoCreate){
43156         if(typeof config.autoCreate == "object"){
43157             if(!config.autoCreate.id){
43158                 config.autoCreate.id = config.id||el;
43159             }
43160             this.el = Roo.DomHelper.append(document.body,
43161                         config.autoCreate, true);
43162         }else{
43163             var elcfg =  {
43164                 tag: "div",
43165                 cls: (config.cls || '') +
43166                     (config.background ? ' bg-' + config.background : '') +
43167                     " roo-layout-inactive-content",
43168                 id: config.id||el
43169             };
43170             if (config.iframe) {
43171                 elcfg.cn = [
43172                     {
43173                         tag : 'iframe',
43174                         style : 'border: 0px',
43175                         src : 'about:blank'
43176                     }
43177                 ];
43178             }
43179               
43180             if (config.html) {
43181                 elcfg.html = config.html;
43182                 
43183             }
43184                         
43185             this.el = Roo.DomHelper.append(document.body, elcfg , true);
43186             if (config.iframe) {
43187                 this.iframeEl = this.el.select('iframe',true).first();
43188             }
43189             
43190         }
43191     } 
43192     this.closable = false;
43193     this.loaded = false;
43194     this.active = false;
43195    
43196       
43197     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
43198         
43199         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
43200         
43201         this.wrapEl = this.el; //this.el.wrap();
43202         var ti = [];
43203         if (config.toolbar.items) {
43204             ti = config.toolbar.items ;
43205             delete config.toolbar.items ;
43206         }
43207         
43208         var nitems = [];
43209         this.toolbar.render(this.wrapEl, 'before');
43210         for(var i =0;i < ti.length;i++) {
43211           //  Roo.log(['add child', items[i]]);
43212             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43213         }
43214         this.toolbar.items = nitems;
43215         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
43216         delete config.toolbar;
43217         
43218     }
43219     /*
43220     // xtype created footer. - not sure if will work as we normally have to render first..
43221     if (this.footer && !this.footer.el && this.footer.xtype) {
43222         if (!this.wrapEl) {
43223             this.wrapEl = this.el.wrap();
43224         }
43225     
43226         this.footer.container = this.wrapEl.createChild();
43227          
43228         this.footer = Roo.factory(this.footer, Roo);
43229         
43230     }
43231     */
43232     
43233      if(typeof config == "string"){
43234         this.title = config;
43235     }else{
43236         Roo.apply(this, config);
43237     }
43238     
43239     if(this.resizeEl){
43240         this.resizeEl = Roo.get(this.resizeEl, true);
43241     }else{
43242         this.resizeEl = this.el;
43243     }
43244     // handle view.xtype
43245     
43246  
43247     
43248     
43249     this.addEvents({
43250         /**
43251          * @event activate
43252          * Fires when this panel is activated. 
43253          * @param {Roo.ContentPanel} this
43254          */
43255         "activate" : true,
43256         /**
43257          * @event deactivate
43258          * Fires when this panel is activated. 
43259          * @param {Roo.ContentPanel} this
43260          */
43261         "deactivate" : true,
43262
43263         /**
43264          * @event resize
43265          * Fires when this panel is resized if fitToFrame is true.
43266          * @param {Roo.ContentPanel} this
43267          * @param {Number} width The width after any component adjustments
43268          * @param {Number} height The height after any component adjustments
43269          */
43270         "resize" : true,
43271         
43272          /**
43273          * @event render
43274          * Fires when this tab is created
43275          * @param {Roo.ContentPanel} this
43276          */
43277         "render" : true,
43278         
43279           /**
43280          * @event scroll
43281          * Fires when this content is scrolled
43282          * @param {Roo.ContentPanel} this
43283          * @param {Event} scrollEvent
43284          */
43285         "scroll" : true
43286         
43287         
43288         
43289     });
43290     
43291
43292     
43293     
43294     if(this.autoScroll && !this.iframe){
43295         this.resizeEl.setStyle("overflow", "auto");
43296         this.resizeEl.on('scroll', this.onScroll, this);
43297     } else {
43298         // fix randome scrolling
43299         //this.el.on('scroll', function() {
43300         //    Roo.log('fix random scolling');
43301         //    this.scrollTo('top',0); 
43302         //});
43303     }
43304     content = content || this.content;
43305     if(content){
43306         this.setContent(content);
43307     }
43308     if(config && config.url){
43309         this.setUrl(this.url, this.params, this.loadOnce);
43310     }
43311     
43312     
43313     
43314     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
43315     
43316     if (this.view && typeof(this.view.xtype) != 'undefined') {
43317         this.view.el = this.el.appendChild(document.createElement("div"));
43318         this.view = Roo.factory(this.view); 
43319         this.view.render  &&  this.view.render(false, '');  
43320     }
43321     
43322     
43323     this.fireEvent('render', this);
43324 };
43325
43326 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
43327     
43328     cls : '',
43329     background : '',
43330     
43331     tabTip : '',
43332     
43333     iframe : false,
43334     iframeEl : false,
43335     
43336     /* Resize Element - use this to work out scroll etc. */
43337     resizeEl : false,
43338     
43339     setRegion : function(region){
43340         this.region = region;
43341         this.setActiveClass(region && !this.background);
43342     },
43343     
43344     
43345     setActiveClass: function(state)
43346     {
43347         if(state){
43348            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
43349            this.el.setStyle('position','relative');
43350         }else{
43351            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
43352            this.el.setStyle('position', 'absolute');
43353         } 
43354     },
43355     
43356     /**
43357      * Returns the toolbar for this Panel if one was configured. 
43358      * @return {Roo.Toolbar} 
43359      */
43360     getToolbar : function(){
43361         return this.toolbar;
43362     },
43363     
43364     setActiveState : function(active)
43365     {
43366         this.active = active;
43367         this.setActiveClass(active);
43368         if(!active){
43369             if(this.fireEvent("deactivate", this) === false){
43370                 return false;
43371             }
43372             return true;
43373         }
43374         this.fireEvent("activate", this);
43375         return true;
43376     },
43377     /**
43378      * Updates this panel's element (not for iframe)
43379      * @param {String} content The new content
43380      * @param {Boolean} loadScripts (optional) true to look for and process scripts
43381     */
43382     setContent : function(content, loadScripts){
43383         if (this.iframe) {
43384             return;
43385         }
43386         
43387         this.el.update(content, loadScripts);
43388     },
43389
43390     ignoreResize : function(w, h)
43391     {
43392         //return false; // always resize?
43393         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
43394             return true;
43395         }else{
43396             this.lastSize = {width: w, height: h};
43397             return false;
43398         }
43399     },
43400     /**
43401      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
43402      * @return {Roo.UpdateManager} The UpdateManager
43403      */
43404     getUpdateManager : function(){
43405         if (this.iframe) {
43406             return false;
43407         }
43408         return this.el.getUpdateManager();
43409     },
43410      /**
43411      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
43412      * Does not work with IFRAME contents
43413      * @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:
43414 <pre><code>
43415 panel.load({
43416     url: "your-url.php",
43417     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
43418     callback: yourFunction,
43419     scope: yourObject, //(optional scope)
43420     discardUrl: false,
43421     nocache: false,
43422     text: "Loading...",
43423     timeout: 30,
43424     scripts: false
43425 });
43426 </code></pre>
43427      
43428      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
43429      * 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.
43430      * @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}
43431      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
43432      * @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.
43433      * @return {Roo.ContentPanel} this
43434      */
43435     load : function(){
43436         
43437         if (this.iframe) {
43438             return this;
43439         }
43440         
43441         var um = this.el.getUpdateManager();
43442         um.update.apply(um, arguments);
43443         return this;
43444     },
43445
43446
43447     /**
43448      * 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.
43449      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
43450      * @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)
43451      * @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)
43452      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
43453      */
43454     setUrl : function(url, params, loadOnce){
43455         if (this.iframe) {
43456             this.iframeEl.dom.src = url;
43457             return false;
43458         }
43459         
43460         if(this.refreshDelegate){
43461             this.removeListener("activate", this.refreshDelegate);
43462         }
43463         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
43464         this.on("activate", this.refreshDelegate);
43465         return this.el.getUpdateManager();
43466     },
43467     
43468     _handleRefresh : function(url, params, loadOnce){
43469         if(!loadOnce || !this.loaded){
43470             var updater = this.el.getUpdateManager();
43471             updater.update(url, params, this._setLoaded.createDelegate(this));
43472         }
43473     },
43474     
43475     _setLoaded : function(){
43476         this.loaded = true;
43477     }, 
43478     
43479     /**
43480      * Returns this panel's id
43481      * @return {String} 
43482      */
43483     getId : function(){
43484         return this.el.id;
43485     },
43486     
43487     /** 
43488      * Returns this panel's element - used by regiosn to add.
43489      * @return {Roo.Element} 
43490      */
43491     getEl : function(){
43492         return this.wrapEl || this.el;
43493     },
43494     
43495    
43496     
43497     adjustForComponents : function(width, height)
43498     {
43499         //Roo.log('adjustForComponents ');
43500         if(this.resizeEl != this.el){
43501             width -= this.el.getFrameWidth('lr');
43502             height -= this.el.getFrameWidth('tb');
43503         }
43504         if(this.toolbar){
43505             var te = this.toolbar.getEl();
43506             te.setWidth(width);
43507             height -= te.getHeight();
43508         }
43509         if(this.footer){
43510             var te = this.footer.getEl();
43511             te.setWidth(width);
43512             height -= te.getHeight();
43513         }
43514         
43515         
43516         if(this.adjustments){
43517             width += this.adjustments[0];
43518             height += this.adjustments[1];
43519         }
43520         return {"width": width, "height": height};
43521     },
43522     
43523     setSize : function(width, height){
43524         if(this.fitToFrame && !this.ignoreResize(width, height)){
43525             if(this.fitContainer && this.resizeEl != this.el){
43526                 this.el.setSize(width, height);
43527             }
43528             var size = this.adjustForComponents(width, height);
43529             if (this.iframe) {
43530                 this.iframeEl.setSize(width,height);
43531             }
43532             
43533             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
43534             this.fireEvent('resize', this, size.width, size.height);
43535             
43536             
43537         }
43538     },
43539     
43540     /**
43541      * Returns this panel's title
43542      * @return {String} 
43543      */
43544     getTitle : function(){
43545         
43546         if (typeof(this.title) != 'object') {
43547             return this.title;
43548         }
43549         
43550         var t = '';
43551         for (var k in this.title) {
43552             if (!this.title.hasOwnProperty(k)) {
43553                 continue;
43554             }
43555             
43556             if (k.indexOf('-') >= 0) {
43557                 var s = k.split('-');
43558                 for (var i = 0; i<s.length; i++) {
43559                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
43560                 }
43561             } else {
43562                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
43563             }
43564         }
43565         return t;
43566     },
43567     
43568     /**
43569      * Set this panel's title
43570      * @param {String} title
43571      */
43572     setTitle : function(title){
43573         this.title = title;
43574         if(this.region){
43575             this.region.updatePanelTitle(this, title);
43576         }
43577     },
43578     
43579     /**
43580      * Returns true is this panel was configured to be closable
43581      * @return {Boolean} 
43582      */
43583     isClosable : function(){
43584         return this.closable;
43585     },
43586     
43587     beforeSlide : function(){
43588         this.el.clip();
43589         this.resizeEl.clip();
43590     },
43591     
43592     afterSlide : function(){
43593         this.el.unclip();
43594         this.resizeEl.unclip();
43595     },
43596     
43597     /**
43598      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
43599      *   Will fail silently if the {@link #setUrl} method has not been called.
43600      *   This does not activate the panel, just updates its content.
43601      */
43602     refresh : function(){
43603         if(this.refreshDelegate){
43604            this.loaded = false;
43605            this.refreshDelegate();
43606         }
43607     },
43608     
43609     /**
43610      * Destroys this panel
43611      */
43612     destroy : function(){
43613         this.el.removeAllListeners();
43614         var tempEl = document.createElement("span");
43615         tempEl.appendChild(this.el.dom);
43616         tempEl.innerHTML = "";
43617         this.el.remove();
43618         this.el = null;
43619     },
43620     
43621     /**
43622      * form - if the content panel contains a form - this is a reference to it.
43623      * @type {Roo.form.Form}
43624      */
43625     form : false,
43626     /**
43627      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
43628      *    This contains a reference to it.
43629      * @type {Roo.View}
43630      */
43631     view : false,
43632     
43633       /**
43634      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
43635      * <pre><code>
43636
43637 layout.addxtype({
43638        xtype : 'Form',
43639        items: [ .... ]
43640    }
43641 );
43642
43643 </code></pre>
43644      * @param {Object} cfg Xtype definition of item to add.
43645      */
43646     
43647     
43648     getChildContainer: function () {
43649         return this.getEl();
43650     },
43651     
43652     
43653     onScroll : function(e)
43654     {
43655         this.fireEvent('scroll', this, e);
43656     }
43657     
43658     
43659     /*
43660         var  ret = new Roo.factory(cfg);
43661         return ret;
43662         
43663         
43664         // add form..
43665         if (cfg.xtype.match(/^Form$/)) {
43666             
43667             var el;
43668             //if (this.footer) {
43669             //    el = this.footer.container.insertSibling(false, 'before');
43670             //} else {
43671                 el = this.el.createChild();
43672             //}
43673
43674             this.form = new  Roo.form.Form(cfg);
43675             
43676             
43677             if ( this.form.allItems.length) {
43678                 this.form.render(el.dom);
43679             }
43680             return this.form;
43681         }
43682         // should only have one of theses..
43683         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
43684             // views.. should not be just added - used named prop 'view''
43685             
43686             cfg.el = this.el.appendChild(document.createElement("div"));
43687             // factory?
43688             
43689             var ret = new Roo.factory(cfg);
43690              
43691              ret.render && ret.render(false, ''); // render blank..
43692             this.view = ret;
43693             return ret;
43694         }
43695         return false;
43696     }
43697     \*/
43698 });
43699  
43700 /**
43701  * @class Roo.bootstrap.panel.Grid
43702  * @extends Roo.bootstrap.panel.Content
43703  * @constructor
43704  * Create a new GridPanel.
43705  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
43706  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
43707  * @param {Object} config A the config object
43708   
43709  */
43710
43711
43712
43713 Roo.bootstrap.panel.Grid = function(config)
43714 {
43715     
43716       
43717     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
43718         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
43719
43720     config.el = this.wrapper;
43721     //this.el = this.wrapper;
43722     
43723       if (config.container) {
43724         // ctor'ed from a Border/panel.grid
43725         
43726         
43727         this.wrapper.setStyle("overflow", "hidden");
43728         this.wrapper.addClass('roo-grid-container');
43729
43730     }
43731     
43732     
43733     if(config.toolbar){
43734         var tool_el = this.wrapper.createChild();    
43735         this.toolbar = Roo.factory(config.toolbar);
43736         var ti = [];
43737         if (config.toolbar.items) {
43738             ti = config.toolbar.items ;
43739             delete config.toolbar.items ;
43740         }
43741         
43742         var nitems = [];
43743         this.toolbar.render(tool_el);
43744         for(var i =0;i < ti.length;i++) {
43745           //  Roo.log(['add child', items[i]]);
43746             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43747         }
43748         this.toolbar.items = nitems;
43749         
43750         delete config.toolbar;
43751     }
43752     
43753     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
43754     config.grid.scrollBody = true;;
43755     config.grid.monitorWindowResize = false; // turn off autosizing
43756     config.grid.autoHeight = false;
43757     config.grid.autoWidth = false;
43758     
43759     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
43760     
43761     if (config.background) {
43762         // render grid on panel activation (if panel background)
43763         this.on('activate', function(gp) {
43764             if (!gp.grid.rendered) {
43765                 gp.grid.render(this.wrapper);
43766                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
43767             }
43768         });
43769             
43770     } else {
43771         this.grid.render(this.wrapper);
43772         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
43773
43774     }
43775     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
43776     // ??? needed ??? config.el = this.wrapper;
43777     
43778     
43779     
43780   
43781     // xtype created footer. - not sure if will work as we normally have to render first..
43782     if (this.footer && !this.footer.el && this.footer.xtype) {
43783         
43784         var ctr = this.grid.getView().getFooterPanel(true);
43785         this.footer.dataSource = this.grid.dataSource;
43786         this.footer = Roo.factory(this.footer, Roo);
43787         this.footer.render(ctr);
43788         
43789     }
43790     
43791     
43792     
43793     
43794      
43795 };
43796
43797 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
43798 {
43799   
43800     getId : function(){
43801         return this.grid.id;
43802     },
43803     
43804     /**
43805      * Returns the grid for this panel
43806      * @return {Roo.bootstrap.Table} 
43807      */
43808     getGrid : function(){
43809         return this.grid;    
43810     },
43811     
43812     setSize : function(width, height)
43813     {
43814      
43815         //if(!this.ignoreResize(width, height)){
43816             var grid = this.grid;
43817             var size = this.adjustForComponents(width, height);
43818             // tfoot is not a footer?
43819           
43820             
43821             var gridel = grid.getGridEl();
43822             gridel.setSize(size.width, size.height);
43823             
43824             var tbd = grid.getGridEl().select('tbody', true).first();
43825             var thd = grid.getGridEl().select('thead',true).first();
43826             var tbf= grid.getGridEl().select('tfoot', true).first();
43827
43828             if (tbf) {
43829                 size.height -= tbf.getHeight();
43830             }
43831             if (thd) {
43832                 size.height -= thd.getHeight();
43833             }
43834             
43835             tbd.setSize(size.width, size.height );
43836             // this is for the account management tab -seems to work there.
43837             var thd = grid.getGridEl().select('thead',true).first();
43838             //if (tbd) {
43839             //    tbd.setSize(size.width, size.height - thd.getHeight());
43840             //}
43841              
43842             grid.autoSize();
43843         //}
43844    
43845     },
43846      
43847     
43848     
43849     beforeSlide : function(){
43850         this.grid.getView().scroller.clip();
43851     },
43852     
43853     afterSlide : function(){
43854         this.grid.getView().scroller.unclip();
43855     },
43856     
43857     destroy : function(){
43858         this.grid.destroy();
43859         delete this.grid;
43860         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
43861     }
43862 });
43863
43864 /**
43865  * @class Roo.bootstrap.panel.Nest
43866  * @extends Roo.bootstrap.panel.Content
43867  * @constructor
43868  * Create a new Panel, that can contain a layout.Border.
43869  * 
43870  * 
43871  * @param {String/Object} config A string to set only the title or a config object
43872  */
43873 Roo.bootstrap.panel.Nest = function(config)
43874 {
43875     // construct with only one argument..
43876     /* FIXME - implement nicer consturctors
43877     if (layout.layout) {
43878         config = layout;
43879         layout = config.layout;
43880         delete config.layout;
43881     }
43882     if (layout.xtype && !layout.getEl) {
43883         // then layout needs constructing..
43884         layout = Roo.factory(layout, Roo);
43885     }
43886     */
43887     
43888     config.el =  config.layout.getEl();
43889     
43890     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
43891     
43892     config.layout.monitorWindowResize = false; // turn off autosizing
43893     this.layout = config.layout;
43894     this.layout.getEl().addClass("roo-layout-nested-layout");
43895     this.layout.parent = this;
43896     
43897     
43898     
43899     
43900 };
43901
43902 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
43903     /**
43904     * @cfg {Roo.BorderLayout} layout The layout for this panel
43905     */
43906     layout : false,
43907
43908     setSize : function(width, height){
43909         if(!this.ignoreResize(width, height)){
43910             var size = this.adjustForComponents(width, height);
43911             var el = this.layout.getEl();
43912             if (size.height < 1) {
43913                 el.setWidth(size.width);   
43914             } else {
43915                 el.setSize(size.width, size.height);
43916             }
43917             var touch = el.dom.offsetWidth;
43918             this.layout.layout();
43919             // ie requires a double layout on the first pass
43920             if(Roo.isIE && !this.initialized){
43921                 this.initialized = true;
43922                 this.layout.layout();
43923             }
43924         }
43925     },
43926     
43927     // activate all subpanels if not currently active..
43928     
43929     setActiveState : function(active){
43930         this.active = active;
43931         this.setActiveClass(active);
43932         
43933         if(!active){
43934             this.fireEvent("deactivate", this);
43935             return;
43936         }
43937         
43938         this.fireEvent("activate", this);
43939         // not sure if this should happen before or after..
43940         if (!this.layout) {
43941             return; // should not happen..
43942         }
43943         var reg = false;
43944         for (var r in this.layout.regions) {
43945             reg = this.layout.getRegion(r);
43946             if (reg.getActivePanel()) {
43947                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
43948                 reg.setActivePanel(reg.getActivePanel());
43949                 continue;
43950             }
43951             if (!reg.panels.length) {
43952                 continue;
43953             }
43954             reg.showPanel(reg.getPanel(0));
43955         }
43956         
43957         
43958         
43959         
43960     },
43961     
43962     /**
43963      * Returns the nested BorderLayout for this panel
43964      * @return {Roo.BorderLayout} 
43965      */
43966     getLayout : function(){
43967         return this.layout;
43968     },
43969     
43970      /**
43971      * Adds a xtype elements to the layout of the nested panel
43972      * <pre><code>
43973
43974 panel.addxtype({
43975        xtype : 'ContentPanel',
43976        region: 'west',
43977        items: [ .... ]
43978    }
43979 );
43980
43981 panel.addxtype({
43982         xtype : 'NestedLayoutPanel',
43983         region: 'west',
43984         layout: {
43985            center: { },
43986            west: { }   
43987         },
43988         items : [ ... list of content panels or nested layout panels.. ]
43989    }
43990 );
43991 </code></pre>
43992      * @param {Object} cfg Xtype definition of item to add.
43993      */
43994     addxtype : function(cfg) {
43995         return this.layout.addxtype(cfg);
43996     
43997     }
43998 });/*
43999  * Based on:
44000  * Ext JS Library 1.1.1
44001  * Copyright(c) 2006-2007, Ext JS, LLC.
44002  *
44003  * Originally Released Under LGPL - original licence link has changed is not relivant.
44004  *
44005  * Fork - LGPL
44006  * <script type="text/javascript">
44007  */
44008 /**
44009  * @class Roo.TabPanel
44010  * @extends Roo.util.Observable
44011  * A lightweight tab container.
44012  * <br><br>
44013  * Usage:
44014  * <pre><code>
44015 // basic tabs 1, built from existing content
44016 var tabs = new Roo.TabPanel("tabs1");
44017 tabs.addTab("script", "View Script");
44018 tabs.addTab("markup", "View Markup");
44019 tabs.activate("script");
44020
44021 // more advanced tabs, built from javascript
44022 var jtabs = new Roo.TabPanel("jtabs");
44023 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
44024
44025 // set up the UpdateManager
44026 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
44027 var updater = tab2.getUpdateManager();
44028 updater.setDefaultUrl("ajax1.htm");
44029 tab2.on('activate', updater.refresh, updater, true);
44030
44031 // Use setUrl for Ajax loading
44032 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
44033 tab3.setUrl("ajax2.htm", null, true);
44034
44035 // Disabled tab
44036 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
44037 tab4.disable();
44038
44039 jtabs.activate("jtabs-1");
44040  * </code></pre>
44041  * @constructor
44042  * Create a new TabPanel.
44043  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
44044  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
44045  */
44046 Roo.bootstrap.panel.Tabs = function(config){
44047     /**
44048     * The container element for this TabPanel.
44049     * @type Roo.Element
44050     */
44051     this.el = Roo.get(config.el);
44052     delete config.el;
44053     if(config){
44054         if(typeof config == "boolean"){
44055             this.tabPosition = config ? "bottom" : "top";
44056         }else{
44057             Roo.apply(this, config);
44058         }
44059     }
44060     
44061     if(this.tabPosition == "bottom"){
44062         // if tabs are at the bottom = create the body first.
44063         this.bodyEl = Roo.get(this.createBody(this.el.dom));
44064         this.el.addClass("roo-tabs-bottom");
44065     }
44066     // next create the tabs holders
44067     
44068     if (this.tabPosition == "west"){
44069         
44070         var reg = this.region; // fake it..
44071         while (reg) {
44072             if (!reg.mgr.parent) {
44073                 break;
44074             }
44075             reg = reg.mgr.parent.region;
44076         }
44077         Roo.log("got nest?");
44078         Roo.log(reg);
44079         if (reg.mgr.getRegion('west')) {
44080             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
44081             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
44082             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
44083             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
44084             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
44085         
44086             
44087         }
44088         
44089         
44090     } else {
44091      
44092         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
44093         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
44094         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
44095         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
44096     }
44097     
44098     
44099     if(Roo.isIE){
44100         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
44101     }
44102     
44103     // finally - if tabs are at the top, then create the body last..
44104     if(this.tabPosition != "bottom"){
44105         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
44106          * @type Roo.Element
44107          */
44108         this.bodyEl = Roo.get(this.createBody(this.el.dom));
44109         this.el.addClass("roo-tabs-top");
44110     }
44111     this.items = [];
44112
44113     this.bodyEl.setStyle("position", "relative");
44114
44115     this.active = null;
44116     this.activateDelegate = this.activate.createDelegate(this);
44117
44118     this.addEvents({
44119         /**
44120          * @event tabchange
44121          * Fires when the active tab changes
44122          * @param {Roo.TabPanel} this
44123          * @param {Roo.TabPanelItem} activePanel The new active tab
44124          */
44125         "tabchange": true,
44126         /**
44127          * @event beforetabchange
44128          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
44129          * @param {Roo.TabPanel} this
44130          * @param {Object} e Set cancel to true on this object to cancel the tab change
44131          * @param {Roo.TabPanelItem} tab The tab being changed to
44132          */
44133         "beforetabchange" : true
44134     });
44135
44136     Roo.EventManager.onWindowResize(this.onResize, this);
44137     this.cpad = this.el.getPadding("lr");
44138     this.hiddenCount = 0;
44139
44140
44141     // toolbar on the tabbar support...
44142     if (this.toolbar) {
44143         alert("no toolbar support yet");
44144         this.toolbar  = false;
44145         /*
44146         var tcfg = this.toolbar;
44147         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
44148         this.toolbar = new Roo.Toolbar(tcfg);
44149         if (Roo.isSafari) {
44150             var tbl = tcfg.container.child('table', true);
44151             tbl.setAttribute('width', '100%');
44152         }
44153         */
44154         
44155     }
44156    
44157
44158
44159     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
44160 };
44161
44162 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
44163     /*
44164      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
44165      */
44166     tabPosition : "top",
44167     /*
44168      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
44169      */
44170     currentTabWidth : 0,
44171     /*
44172      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
44173      */
44174     minTabWidth : 40,
44175     /*
44176      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
44177      */
44178     maxTabWidth : 250,
44179     /*
44180      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
44181      */
44182     preferredTabWidth : 175,
44183     /*
44184      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
44185      */
44186     resizeTabs : false,
44187     /*
44188      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
44189      */
44190     monitorResize : true,
44191     /*
44192      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
44193      */
44194     toolbar : false,  // set by caller..
44195     
44196     region : false, /// set by caller
44197     
44198     disableTooltips : true, // not used yet...
44199
44200     /**
44201      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
44202      * @param {String} id The id of the div to use <b>or create</b>
44203      * @param {String} text The text for the tab
44204      * @param {String} content (optional) Content to put in the TabPanelItem body
44205      * @param {Boolean} closable (optional) True to create a close icon on the tab
44206      * @return {Roo.TabPanelItem} The created TabPanelItem
44207      */
44208     addTab : function(id, text, content, closable, tpl)
44209     {
44210         var item = new Roo.bootstrap.panel.TabItem({
44211             panel: this,
44212             id : id,
44213             text : text,
44214             closable : closable,
44215             tpl : tpl
44216         });
44217         this.addTabItem(item);
44218         if(content){
44219             item.setContent(content);
44220         }
44221         return item;
44222     },
44223
44224     /**
44225      * Returns the {@link Roo.TabPanelItem} with the specified id/index
44226      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
44227      * @return {Roo.TabPanelItem}
44228      */
44229     getTab : function(id){
44230         return this.items[id];
44231     },
44232
44233     /**
44234      * Hides the {@link Roo.TabPanelItem} with the specified id/index
44235      * @param {String/Number} id The id or index of the TabPanelItem to hide.
44236      */
44237     hideTab : function(id){
44238         var t = this.items[id];
44239         if(!t.isHidden()){
44240            t.setHidden(true);
44241            this.hiddenCount++;
44242            this.autoSizeTabs();
44243         }
44244     },
44245
44246     /**
44247      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
44248      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
44249      */
44250     unhideTab : function(id){
44251         var t = this.items[id];
44252         if(t.isHidden()){
44253            t.setHidden(false);
44254            this.hiddenCount--;
44255            this.autoSizeTabs();
44256         }
44257     },
44258
44259     /**
44260      * Adds an existing {@link Roo.TabPanelItem}.
44261      * @param {Roo.TabPanelItem} item The TabPanelItem to add
44262      */
44263     addTabItem : function(item)
44264     {
44265         this.items[item.id] = item;
44266         this.items.push(item);
44267         this.autoSizeTabs();
44268       //  if(this.resizeTabs){
44269     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
44270   //         this.autoSizeTabs();
44271 //        }else{
44272 //            item.autoSize();
44273        // }
44274     },
44275
44276     /**
44277      * Removes a {@link Roo.TabPanelItem}.
44278      * @param {String/Number} id The id or index of the TabPanelItem to remove.
44279      */
44280     removeTab : function(id){
44281         var items = this.items;
44282         var tab = items[id];
44283         if(!tab) { return; }
44284         var index = items.indexOf(tab);
44285         if(this.active == tab && items.length > 1){
44286             var newTab = this.getNextAvailable(index);
44287             if(newTab) {
44288                 newTab.activate();
44289             }
44290         }
44291         this.stripEl.dom.removeChild(tab.pnode.dom);
44292         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
44293             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
44294         }
44295         items.splice(index, 1);
44296         delete this.items[tab.id];
44297         tab.fireEvent("close", tab);
44298         tab.purgeListeners();
44299         this.autoSizeTabs();
44300     },
44301
44302     getNextAvailable : function(start){
44303         var items = this.items;
44304         var index = start;
44305         // look for a next tab that will slide over to
44306         // replace the one being removed
44307         while(index < items.length){
44308             var item = items[++index];
44309             if(item && !item.isHidden()){
44310                 return item;
44311             }
44312         }
44313         // if one isn't found select the previous tab (on the left)
44314         index = start;
44315         while(index >= 0){
44316             var item = items[--index];
44317             if(item && !item.isHidden()){
44318                 return item;
44319             }
44320         }
44321         return null;
44322     },
44323
44324     /**
44325      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
44326      * @param {String/Number} id The id or index of the TabPanelItem to disable.
44327      */
44328     disableTab : function(id){
44329         var tab = this.items[id];
44330         if(tab && this.active != tab){
44331             tab.disable();
44332         }
44333     },
44334
44335     /**
44336      * Enables a {@link Roo.TabPanelItem} that is disabled.
44337      * @param {String/Number} id The id or index of the TabPanelItem to enable.
44338      */
44339     enableTab : function(id){
44340         var tab = this.items[id];
44341         tab.enable();
44342     },
44343
44344     /**
44345      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
44346      * @param {String/Number} id The id or index of the TabPanelItem to activate.
44347      * @return {Roo.TabPanelItem} The TabPanelItem.
44348      */
44349     activate : function(id)
44350     {
44351         //Roo.log('activite:'  + id);
44352         
44353         var tab = this.items[id];
44354         if(!tab){
44355             return null;
44356         }
44357         if(tab == this.active || tab.disabled){
44358             return tab;
44359         }
44360         var e = {};
44361         this.fireEvent("beforetabchange", this, e, tab);
44362         if(e.cancel !== true && !tab.disabled){
44363             if(this.active){
44364                 this.active.hide();
44365             }
44366             this.active = this.items[id];
44367             this.active.show();
44368             this.fireEvent("tabchange", this, this.active);
44369         }
44370         return tab;
44371     },
44372
44373     /**
44374      * Gets the active {@link Roo.TabPanelItem}.
44375      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
44376      */
44377     getActiveTab : function(){
44378         return this.active;
44379     },
44380
44381     /**
44382      * Updates the tab body element to fit the height of the container element
44383      * for overflow scrolling
44384      * @param {Number} targetHeight (optional) Override the starting height from the elements height
44385      */
44386     syncHeight : function(targetHeight){
44387         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44388         var bm = this.bodyEl.getMargins();
44389         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
44390         this.bodyEl.setHeight(newHeight);
44391         return newHeight;
44392     },
44393
44394     onResize : function(){
44395         if(this.monitorResize){
44396             this.autoSizeTabs();
44397         }
44398     },
44399
44400     /**
44401      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
44402      */
44403     beginUpdate : function(){
44404         this.updating = true;
44405     },
44406
44407     /**
44408      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
44409      */
44410     endUpdate : function(){
44411         this.updating = false;
44412         this.autoSizeTabs();
44413     },
44414
44415     /**
44416      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
44417      */
44418     autoSizeTabs : function()
44419     {
44420         var count = this.items.length;
44421         var vcount = count - this.hiddenCount;
44422         
44423         if (vcount < 2) {
44424             this.stripEl.hide();
44425         } else {
44426             this.stripEl.show();
44427         }
44428         
44429         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
44430             return;
44431         }
44432         
44433         
44434         var w = Math.max(this.el.getWidth() - this.cpad, 10);
44435         var availWidth = Math.floor(w / vcount);
44436         var b = this.stripBody;
44437         if(b.getWidth() > w){
44438             var tabs = this.items;
44439             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
44440             if(availWidth < this.minTabWidth){
44441                 /*if(!this.sleft){    // incomplete scrolling code
44442                     this.createScrollButtons();
44443                 }
44444                 this.showScroll();
44445                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
44446             }
44447         }else{
44448             if(this.currentTabWidth < this.preferredTabWidth){
44449                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
44450             }
44451         }
44452     },
44453
44454     /**
44455      * Returns the number of tabs in this TabPanel.
44456      * @return {Number}
44457      */
44458      getCount : function(){
44459          return this.items.length;
44460      },
44461
44462     /**
44463      * Resizes all the tabs to the passed width
44464      * @param {Number} The new width
44465      */
44466     setTabWidth : function(width){
44467         this.currentTabWidth = width;
44468         for(var i = 0, len = this.items.length; i < len; i++) {
44469                 if(!this.items[i].isHidden()) {
44470                 this.items[i].setWidth(width);
44471             }
44472         }
44473     },
44474
44475     /**
44476      * Destroys this TabPanel
44477      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
44478      */
44479     destroy : function(removeEl){
44480         Roo.EventManager.removeResizeListener(this.onResize, this);
44481         for(var i = 0, len = this.items.length; i < len; i++){
44482             this.items[i].purgeListeners();
44483         }
44484         if(removeEl === true){
44485             this.el.update("");
44486             this.el.remove();
44487         }
44488     },
44489     
44490     createStrip : function(container)
44491     {
44492         var strip = document.createElement("nav");
44493         strip.className = Roo.bootstrap.version == 4 ?
44494             "navbar-light bg-light" : 
44495             "navbar navbar-default"; //"x-tabs-wrap";
44496         container.appendChild(strip);
44497         return strip;
44498     },
44499     
44500     createStripList : function(strip)
44501     {
44502         // div wrapper for retard IE
44503         // returns the "tr" element.
44504         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
44505         //'<div class="x-tabs-strip-wrap">'+
44506           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
44507           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
44508         return strip.firstChild; //.firstChild.firstChild.firstChild;
44509     },
44510     createBody : function(container)
44511     {
44512         var body = document.createElement("div");
44513         Roo.id(body, "tab-body");
44514         //Roo.fly(body).addClass("x-tabs-body");
44515         Roo.fly(body).addClass("tab-content");
44516         container.appendChild(body);
44517         return body;
44518     },
44519     createItemBody :function(bodyEl, id){
44520         var body = Roo.getDom(id);
44521         if(!body){
44522             body = document.createElement("div");
44523             body.id = id;
44524         }
44525         //Roo.fly(body).addClass("x-tabs-item-body");
44526         Roo.fly(body).addClass("tab-pane");
44527          bodyEl.insertBefore(body, bodyEl.firstChild);
44528         return body;
44529     },
44530     /** @private */
44531     createStripElements :  function(stripEl, text, closable, tpl)
44532     {
44533         var td = document.createElement("li"); // was td..
44534         td.className = 'nav-item';
44535         
44536         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
44537         
44538         
44539         stripEl.appendChild(td);
44540         /*if(closable){
44541             td.className = "x-tabs-closable";
44542             if(!this.closeTpl){
44543                 this.closeTpl = new Roo.Template(
44544                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44545                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
44546                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
44547                 );
44548             }
44549             var el = this.closeTpl.overwrite(td, {"text": text});
44550             var close = el.getElementsByTagName("div")[0];
44551             var inner = el.getElementsByTagName("em")[0];
44552             return {"el": el, "close": close, "inner": inner};
44553         } else {
44554         */
44555         // not sure what this is..
44556 //            if(!this.tabTpl){
44557                 //this.tabTpl = new Roo.Template(
44558                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44559                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
44560                 //);
44561 //                this.tabTpl = new Roo.Template(
44562 //                   '<a href="#">' +
44563 //                   '<span unselectable="on"' +
44564 //                            (this.disableTooltips ? '' : ' title="{text}"') +
44565 //                            ' >{text}</span></a>'
44566 //                );
44567 //                
44568 //            }
44569
44570
44571             var template = tpl || this.tabTpl || false;
44572             
44573             if(!template){
44574                 template =  new Roo.Template(
44575                         Roo.bootstrap.version == 4 ? 
44576                             (
44577                                 '<a class="nav-link" href="#" unselectable="on"' +
44578                                      (this.disableTooltips ? '' : ' title="{text}"') +
44579                                      ' >{text}</a>'
44580                             ) : (
44581                                 '<a class="nav-link" href="#">' +
44582                                 '<span unselectable="on"' +
44583                                          (this.disableTooltips ? '' : ' title="{text}"') +
44584                                     ' >{text}</span></a>'
44585                             )
44586                 );
44587             }
44588             
44589             switch (typeof(template)) {
44590                 case 'object' :
44591                     break;
44592                 case 'string' :
44593                     template = new Roo.Template(template);
44594                     break;
44595                 default :
44596                     break;
44597             }
44598             
44599             var el = template.overwrite(td, {"text": text});
44600             
44601             var inner = el.getElementsByTagName("span")[0];
44602             
44603             return {"el": el, "inner": inner};
44604             
44605     }
44606         
44607     
44608 });
44609
44610 /**
44611  * @class Roo.TabPanelItem
44612  * @extends Roo.util.Observable
44613  * Represents an individual item (tab plus body) in a TabPanel.
44614  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
44615  * @param {String} id The id of this TabPanelItem
44616  * @param {String} text The text for the tab of this TabPanelItem
44617  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
44618  */
44619 Roo.bootstrap.panel.TabItem = function(config){
44620     /**
44621      * The {@link Roo.TabPanel} this TabPanelItem belongs to
44622      * @type Roo.TabPanel
44623      */
44624     this.tabPanel = config.panel;
44625     /**
44626      * The id for this TabPanelItem
44627      * @type String
44628      */
44629     this.id = config.id;
44630     /** @private */
44631     this.disabled = false;
44632     /** @private */
44633     this.text = config.text;
44634     /** @private */
44635     this.loaded = false;
44636     this.closable = config.closable;
44637
44638     /**
44639      * The body element for this TabPanelItem.
44640      * @type Roo.Element
44641      */
44642     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
44643     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
44644     this.bodyEl.setStyle("display", "block");
44645     this.bodyEl.setStyle("zoom", "1");
44646     //this.hideAction();
44647
44648     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
44649     /** @private */
44650     this.el = Roo.get(els.el);
44651     this.inner = Roo.get(els.inner, true);
44652      this.textEl = Roo.bootstrap.version == 4 ?
44653         this.el : Roo.get(this.el.dom.firstChild, true);
44654
44655     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
44656     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
44657
44658     
44659 //    this.el.on("mousedown", this.onTabMouseDown, this);
44660     this.el.on("click", this.onTabClick, this);
44661     /** @private */
44662     if(config.closable){
44663         var c = Roo.get(els.close, true);
44664         c.dom.title = this.closeText;
44665         c.addClassOnOver("close-over");
44666         c.on("click", this.closeClick, this);
44667      }
44668
44669     this.addEvents({
44670          /**
44671          * @event activate
44672          * Fires when this tab becomes the active tab.
44673          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44674          * @param {Roo.TabPanelItem} this
44675          */
44676         "activate": true,
44677         /**
44678          * @event beforeclose
44679          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
44680          * @param {Roo.TabPanelItem} this
44681          * @param {Object} e Set cancel to true on this object to cancel the close.
44682          */
44683         "beforeclose": true,
44684         /**
44685          * @event close
44686          * Fires when this tab is closed.
44687          * @param {Roo.TabPanelItem} this
44688          */
44689          "close": true,
44690         /**
44691          * @event deactivate
44692          * Fires when this tab is no longer the active tab.
44693          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44694          * @param {Roo.TabPanelItem} this
44695          */
44696          "deactivate" : true
44697     });
44698     this.hidden = false;
44699
44700     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
44701 };
44702
44703 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
44704            {
44705     purgeListeners : function(){
44706        Roo.util.Observable.prototype.purgeListeners.call(this);
44707        this.el.removeAllListeners();
44708     },
44709     /**
44710      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
44711      */
44712     show : function(){
44713         this.status_node.addClass("active");
44714         this.showAction();
44715         if(Roo.isOpera){
44716             this.tabPanel.stripWrap.repaint();
44717         }
44718         this.fireEvent("activate", this.tabPanel, this);
44719     },
44720
44721     /**
44722      * Returns true if this tab is the active tab.
44723      * @return {Boolean}
44724      */
44725     isActive : function(){
44726         return this.tabPanel.getActiveTab() == this;
44727     },
44728
44729     /**
44730      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
44731      */
44732     hide : function(){
44733         this.status_node.removeClass("active");
44734         this.hideAction();
44735         this.fireEvent("deactivate", this.tabPanel, this);
44736     },
44737
44738     hideAction : function(){
44739         this.bodyEl.hide();
44740         this.bodyEl.setStyle("position", "absolute");
44741         this.bodyEl.setLeft("-20000px");
44742         this.bodyEl.setTop("-20000px");
44743     },
44744
44745     showAction : function(){
44746         this.bodyEl.setStyle("position", "relative");
44747         this.bodyEl.setTop("");
44748         this.bodyEl.setLeft("");
44749         this.bodyEl.show();
44750     },
44751
44752     /**
44753      * Set the tooltip for the tab.
44754      * @param {String} tooltip The tab's tooltip
44755      */
44756     setTooltip : function(text){
44757         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
44758             this.textEl.dom.qtip = text;
44759             this.textEl.dom.removeAttribute('title');
44760         }else{
44761             this.textEl.dom.title = text;
44762         }
44763     },
44764
44765     onTabClick : function(e){
44766         e.preventDefault();
44767         this.tabPanel.activate(this.id);
44768     },
44769
44770     onTabMouseDown : function(e){
44771         e.preventDefault();
44772         this.tabPanel.activate(this.id);
44773     },
44774 /*
44775     getWidth : function(){
44776         return this.inner.getWidth();
44777     },
44778
44779     setWidth : function(width){
44780         var iwidth = width - this.linode.getPadding("lr");
44781         this.inner.setWidth(iwidth);
44782         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
44783         this.linode.setWidth(width);
44784     },
44785 */
44786     /**
44787      * Show or hide the tab
44788      * @param {Boolean} hidden True to hide or false to show.
44789      */
44790     setHidden : function(hidden){
44791         this.hidden = hidden;
44792         this.linode.setStyle("display", hidden ? "none" : "");
44793     },
44794
44795     /**
44796      * Returns true if this tab is "hidden"
44797      * @return {Boolean}
44798      */
44799     isHidden : function(){
44800         return this.hidden;
44801     },
44802
44803     /**
44804      * Returns the text for this tab
44805      * @return {String}
44806      */
44807     getText : function(){
44808         return this.text;
44809     },
44810     /*
44811     autoSize : function(){
44812         //this.el.beginMeasure();
44813         this.textEl.setWidth(1);
44814         /*
44815          *  #2804 [new] Tabs in Roojs
44816          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
44817          */
44818         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
44819         //this.el.endMeasure();
44820     //},
44821
44822     /**
44823      * Sets the text for the tab (Note: this also sets the tooltip text)
44824      * @param {String} text The tab's text and tooltip
44825      */
44826     setText : function(text){
44827         this.text = text;
44828         this.textEl.update(text);
44829         this.setTooltip(text);
44830         //if(!this.tabPanel.resizeTabs){
44831         //    this.autoSize();
44832         //}
44833     },
44834     /**
44835      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
44836      */
44837     activate : function(){
44838         this.tabPanel.activate(this.id);
44839     },
44840
44841     /**
44842      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
44843      */
44844     disable : function(){
44845         if(this.tabPanel.active != this){
44846             this.disabled = true;
44847             this.status_node.addClass("disabled");
44848         }
44849     },
44850
44851     /**
44852      * Enables this TabPanelItem if it was previously disabled.
44853      */
44854     enable : function(){
44855         this.disabled = false;
44856         this.status_node.removeClass("disabled");
44857     },
44858
44859     /**
44860      * Sets the content for this TabPanelItem.
44861      * @param {String} content The content
44862      * @param {Boolean} loadScripts true to look for and load scripts
44863      */
44864     setContent : function(content, loadScripts){
44865         this.bodyEl.update(content, loadScripts);
44866     },
44867
44868     /**
44869      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
44870      * @return {Roo.UpdateManager} The UpdateManager
44871      */
44872     getUpdateManager : function(){
44873         return this.bodyEl.getUpdateManager();
44874     },
44875
44876     /**
44877      * Set a URL to be used to load the content for this TabPanelItem.
44878      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
44879      * @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)
44880      * @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)
44881      * @return {Roo.UpdateManager} The UpdateManager
44882      */
44883     setUrl : function(url, params, loadOnce){
44884         if(this.refreshDelegate){
44885             this.un('activate', this.refreshDelegate);
44886         }
44887         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44888         this.on("activate", this.refreshDelegate);
44889         return this.bodyEl.getUpdateManager();
44890     },
44891
44892     /** @private */
44893     _handleRefresh : function(url, params, loadOnce){
44894         if(!loadOnce || !this.loaded){
44895             var updater = this.bodyEl.getUpdateManager();
44896             updater.update(url, params, this._setLoaded.createDelegate(this));
44897         }
44898     },
44899
44900     /**
44901      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
44902      *   Will fail silently if the setUrl method has not been called.
44903      *   This does not activate the panel, just updates its content.
44904      */
44905     refresh : function(){
44906         if(this.refreshDelegate){
44907            this.loaded = false;
44908            this.refreshDelegate();
44909         }
44910     },
44911
44912     /** @private */
44913     _setLoaded : function(){
44914         this.loaded = true;
44915     },
44916
44917     /** @private */
44918     closeClick : function(e){
44919         var o = {};
44920         e.stopEvent();
44921         this.fireEvent("beforeclose", this, o);
44922         if(o.cancel !== true){
44923             this.tabPanel.removeTab(this.id);
44924         }
44925     },
44926     /**
44927      * The text displayed in the tooltip for the close icon.
44928      * @type String
44929      */
44930     closeText : "Close this tab"
44931 });
44932 /**
44933 *    This script refer to:
44934 *    Title: International Telephone Input
44935 *    Author: Jack O'Connor
44936 *    Code version:  v12.1.12
44937 *    Availability: https://github.com/jackocnr/intl-tel-input.git
44938 **/
44939
44940 Roo.bootstrap.form.PhoneInputData = function() {
44941     var d = [
44942       [
44943         "Afghanistan (‫افغانستان‬‎)",
44944         "af",
44945         "93"
44946       ],
44947       [
44948         "Albania (Shqipëri)",
44949         "al",
44950         "355"
44951       ],
44952       [
44953         "Algeria (‫الجزائر‬‎)",
44954         "dz",
44955         "213"
44956       ],
44957       [
44958         "American Samoa",
44959         "as",
44960         "1684"
44961       ],
44962       [
44963         "Andorra",
44964         "ad",
44965         "376"
44966       ],
44967       [
44968         "Angola",
44969         "ao",
44970         "244"
44971       ],
44972       [
44973         "Anguilla",
44974         "ai",
44975         "1264"
44976       ],
44977       [
44978         "Antigua and Barbuda",
44979         "ag",
44980         "1268"
44981       ],
44982       [
44983         "Argentina",
44984         "ar",
44985         "54"
44986       ],
44987       [
44988         "Armenia (Հայաստան)",
44989         "am",
44990         "374"
44991       ],
44992       [
44993         "Aruba",
44994         "aw",
44995         "297"
44996       ],
44997       [
44998         "Australia",
44999         "au",
45000         "61",
45001         0
45002       ],
45003       [
45004         "Austria (Österreich)",
45005         "at",
45006         "43"
45007       ],
45008       [
45009         "Azerbaijan (Azərbaycan)",
45010         "az",
45011         "994"
45012       ],
45013       [
45014         "Bahamas",
45015         "bs",
45016         "1242"
45017       ],
45018       [
45019         "Bahrain (‫البحرين‬‎)",
45020         "bh",
45021         "973"
45022       ],
45023       [
45024         "Bangladesh (বাংলাদেশ)",
45025         "bd",
45026         "880"
45027       ],
45028       [
45029         "Barbados",
45030         "bb",
45031         "1246"
45032       ],
45033       [
45034         "Belarus (Беларусь)",
45035         "by",
45036         "375"
45037       ],
45038       [
45039         "Belgium (België)",
45040         "be",
45041         "32"
45042       ],
45043       [
45044         "Belize",
45045         "bz",
45046         "501"
45047       ],
45048       [
45049         "Benin (Bénin)",
45050         "bj",
45051         "229"
45052       ],
45053       [
45054         "Bermuda",
45055         "bm",
45056         "1441"
45057       ],
45058       [
45059         "Bhutan (འབྲུག)",
45060         "bt",
45061         "975"
45062       ],
45063       [
45064         "Bolivia",
45065         "bo",
45066         "591"
45067       ],
45068       [
45069         "Bosnia and Herzegovina (Босна и Херцеговина)",
45070         "ba",
45071         "387"
45072       ],
45073       [
45074         "Botswana",
45075         "bw",
45076         "267"
45077       ],
45078       [
45079         "Brazil (Brasil)",
45080         "br",
45081         "55"
45082       ],
45083       [
45084         "British Indian Ocean Territory",
45085         "io",
45086         "246"
45087       ],
45088       [
45089         "British Virgin Islands",
45090         "vg",
45091         "1284"
45092       ],
45093       [
45094         "Brunei",
45095         "bn",
45096         "673"
45097       ],
45098       [
45099         "Bulgaria (България)",
45100         "bg",
45101         "359"
45102       ],
45103       [
45104         "Burkina Faso",
45105         "bf",
45106         "226"
45107       ],
45108       [
45109         "Burundi (Uburundi)",
45110         "bi",
45111         "257"
45112       ],
45113       [
45114         "Cambodia (កម្ពុជា)",
45115         "kh",
45116         "855"
45117       ],
45118       [
45119         "Cameroon (Cameroun)",
45120         "cm",
45121         "237"
45122       ],
45123       [
45124         "Canada",
45125         "ca",
45126         "1",
45127         1,
45128         ["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"]
45129       ],
45130       [
45131         "Cape Verde (Kabu Verdi)",
45132         "cv",
45133         "238"
45134       ],
45135       [
45136         "Caribbean Netherlands",
45137         "bq",
45138         "599",
45139         1
45140       ],
45141       [
45142         "Cayman Islands",
45143         "ky",
45144         "1345"
45145       ],
45146       [
45147         "Central African Republic (République centrafricaine)",
45148         "cf",
45149         "236"
45150       ],
45151       [
45152         "Chad (Tchad)",
45153         "td",
45154         "235"
45155       ],
45156       [
45157         "Chile",
45158         "cl",
45159         "56"
45160       ],
45161       [
45162         "China (中国)",
45163         "cn",
45164         "86"
45165       ],
45166       [
45167         "Christmas Island",
45168         "cx",
45169         "61",
45170         2
45171       ],
45172       [
45173         "Cocos (Keeling) Islands",
45174         "cc",
45175         "61",
45176         1
45177       ],
45178       [
45179         "Colombia",
45180         "co",
45181         "57"
45182       ],
45183       [
45184         "Comoros (‫جزر القمر‬‎)",
45185         "km",
45186         "269"
45187       ],
45188       [
45189         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
45190         "cd",
45191         "243"
45192       ],
45193       [
45194         "Congo (Republic) (Congo-Brazzaville)",
45195         "cg",
45196         "242"
45197       ],
45198       [
45199         "Cook Islands",
45200         "ck",
45201         "682"
45202       ],
45203       [
45204         "Costa Rica",
45205         "cr",
45206         "506"
45207       ],
45208       [
45209         "Côte d’Ivoire",
45210         "ci",
45211         "225"
45212       ],
45213       [
45214         "Croatia (Hrvatska)",
45215         "hr",
45216         "385"
45217       ],
45218       [
45219         "Cuba",
45220         "cu",
45221         "53"
45222       ],
45223       [
45224         "Curaçao",
45225         "cw",
45226         "599",
45227         0
45228       ],
45229       [
45230         "Cyprus (Κύπρος)",
45231         "cy",
45232         "357"
45233       ],
45234       [
45235         "Czech Republic (Česká republika)",
45236         "cz",
45237         "420"
45238       ],
45239       [
45240         "Denmark (Danmark)",
45241         "dk",
45242         "45"
45243       ],
45244       [
45245         "Djibouti",
45246         "dj",
45247         "253"
45248       ],
45249       [
45250         "Dominica",
45251         "dm",
45252         "1767"
45253       ],
45254       [
45255         "Dominican Republic (República Dominicana)",
45256         "do",
45257         "1",
45258         2,
45259         ["809", "829", "849"]
45260       ],
45261       [
45262         "Ecuador",
45263         "ec",
45264         "593"
45265       ],
45266       [
45267         "Egypt (‫مصر‬‎)",
45268         "eg",
45269         "20"
45270       ],
45271       [
45272         "El Salvador",
45273         "sv",
45274         "503"
45275       ],
45276       [
45277         "Equatorial Guinea (Guinea Ecuatorial)",
45278         "gq",
45279         "240"
45280       ],
45281       [
45282         "Eritrea",
45283         "er",
45284         "291"
45285       ],
45286       [
45287         "Estonia (Eesti)",
45288         "ee",
45289         "372"
45290       ],
45291       [
45292         "Ethiopia",
45293         "et",
45294         "251"
45295       ],
45296       [
45297         "Falkland Islands (Islas Malvinas)",
45298         "fk",
45299         "500"
45300       ],
45301       [
45302         "Faroe Islands (Føroyar)",
45303         "fo",
45304         "298"
45305       ],
45306       [
45307         "Fiji",
45308         "fj",
45309         "679"
45310       ],
45311       [
45312         "Finland (Suomi)",
45313         "fi",
45314         "358",
45315         0
45316       ],
45317       [
45318         "France",
45319         "fr",
45320         "33"
45321       ],
45322       [
45323         "French Guiana (Guyane française)",
45324         "gf",
45325         "594"
45326       ],
45327       [
45328         "French Polynesia (Polynésie française)",
45329         "pf",
45330         "689"
45331       ],
45332       [
45333         "Gabon",
45334         "ga",
45335         "241"
45336       ],
45337       [
45338         "Gambia",
45339         "gm",
45340         "220"
45341       ],
45342       [
45343         "Georgia (საქართველო)",
45344         "ge",
45345         "995"
45346       ],
45347       [
45348         "Germany (Deutschland)",
45349         "de",
45350         "49"
45351       ],
45352       [
45353         "Ghana (Gaana)",
45354         "gh",
45355         "233"
45356       ],
45357       [
45358         "Gibraltar",
45359         "gi",
45360         "350"
45361       ],
45362       [
45363         "Greece (Ελλάδα)",
45364         "gr",
45365         "30"
45366       ],
45367       [
45368         "Greenland (Kalaallit Nunaat)",
45369         "gl",
45370         "299"
45371       ],
45372       [
45373         "Grenada",
45374         "gd",
45375         "1473"
45376       ],
45377       [
45378         "Guadeloupe",
45379         "gp",
45380         "590",
45381         0
45382       ],
45383       [
45384         "Guam",
45385         "gu",
45386         "1671"
45387       ],
45388       [
45389         "Guatemala",
45390         "gt",
45391         "502"
45392       ],
45393       [
45394         "Guernsey",
45395         "gg",
45396         "44",
45397         1
45398       ],
45399       [
45400         "Guinea (Guinée)",
45401         "gn",
45402         "224"
45403       ],
45404       [
45405         "Guinea-Bissau (Guiné Bissau)",
45406         "gw",
45407         "245"
45408       ],
45409       [
45410         "Guyana",
45411         "gy",
45412         "592"
45413       ],
45414       [
45415         "Haiti",
45416         "ht",
45417         "509"
45418       ],
45419       [
45420         "Honduras",
45421         "hn",
45422         "504"
45423       ],
45424       [
45425         "Hong Kong (香港)",
45426         "hk",
45427         "852"
45428       ],
45429       [
45430         "Hungary (Magyarország)",
45431         "hu",
45432         "36"
45433       ],
45434       [
45435         "Iceland (Ísland)",
45436         "is",
45437         "354"
45438       ],
45439       [
45440         "India (भारत)",
45441         "in",
45442         "91"
45443       ],
45444       [
45445         "Indonesia",
45446         "id",
45447         "62"
45448       ],
45449       [
45450         "Iran (‫ایران‬‎)",
45451         "ir",
45452         "98"
45453       ],
45454       [
45455         "Iraq (‫العراق‬‎)",
45456         "iq",
45457         "964"
45458       ],
45459       [
45460         "Ireland",
45461         "ie",
45462         "353"
45463       ],
45464       [
45465         "Isle of Man",
45466         "im",
45467         "44",
45468         2
45469       ],
45470       [
45471         "Israel (‫ישראל‬‎)",
45472         "il",
45473         "972"
45474       ],
45475       [
45476         "Italy (Italia)",
45477         "it",
45478         "39",
45479         0
45480       ],
45481       [
45482         "Jamaica",
45483         "jm",
45484         "1876"
45485       ],
45486       [
45487         "Japan (日本)",
45488         "jp",
45489         "81"
45490       ],
45491       [
45492         "Jersey",
45493         "je",
45494         "44",
45495         3
45496       ],
45497       [
45498         "Jordan (‫الأردن‬‎)",
45499         "jo",
45500         "962"
45501       ],
45502       [
45503         "Kazakhstan (Казахстан)",
45504         "kz",
45505         "7",
45506         1
45507       ],
45508       [
45509         "Kenya",
45510         "ke",
45511         "254"
45512       ],
45513       [
45514         "Kiribati",
45515         "ki",
45516         "686"
45517       ],
45518       [
45519         "Kosovo",
45520         "xk",
45521         "383"
45522       ],
45523       [
45524         "Kuwait (‫الكويت‬‎)",
45525         "kw",
45526         "965"
45527       ],
45528       [
45529         "Kyrgyzstan (Кыргызстан)",
45530         "kg",
45531         "996"
45532       ],
45533       [
45534         "Laos (ລາວ)",
45535         "la",
45536         "856"
45537       ],
45538       [
45539         "Latvia (Latvija)",
45540         "lv",
45541         "371"
45542       ],
45543       [
45544         "Lebanon (‫لبنان‬‎)",
45545         "lb",
45546         "961"
45547       ],
45548       [
45549         "Lesotho",
45550         "ls",
45551         "266"
45552       ],
45553       [
45554         "Liberia",
45555         "lr",
45556         "231"
45557       ],
45558       [
45559         "Libya (‫ليبيا‬‎)",
45560         "ly",
45561         "218"
45562       ],
45563       [
45564         "Liechtenstein",
45565         "li",
45566         "423"
45567       ],
45568       [
45569         "Lithuania (Lietuva)",
45570         "lt",
45571         "370"
45572       ],
45573       [
45574         "Luxembourg",
45575         "lu",
45576         "352"
45577       ],
45578       [
45579         "Macau (澳門)",
45580         "mo",
45581         "853"
45582       ],
45583       [
45584         "Macedonia (FYROM) (Македонија)",
45585         "mk",
45586         "389"
45587       ],
45588       [
45589         "Madagascar (Madagasikara)",
45590         "mg",
45591         "261"
45592       ],
45593       [
45594         "Malawi",
45595         "mw",
45596         "265"
45597       ],
45598       [
45599         "Malaysia",
45600         "my",
45601         "60"
45602       ],
45603       [
45604         "Maldives",
45605         "mv",
45606         "960"
45607       ],
45608       [
45609         "Mali",
45610         "ml",
45611         "223"
45612       ],
45613       [
45614         "Malta",
45615         "mt",
45616         "356"
45617       ],
45618       [
45619         "Marshall Islands",
45620         "mh",
45621         "692"
45622       ],
45623       [
45624         "Martinique",
45625         "mq",
45626         "596"
45627       ],
45628       [
45629         "Mauritania (‫موريتانيا‬‎)",
45630         "mr",
45631         "222"
45632       ],
45633       [
45634         "Mauritius (Moris)",
45635         "mu",
45636         "230"
45637       ],
45638       [
45639         "Mayotte",
45640         "yt",
45641         "262",
45642         1
45643       ],
45644       [
45645         "Mexico (México)",
45646         "mx",
45647         "52"
45648       ],
45649       [
45650         "Micronesia",
45651         "fm",
45652         "691"
45653       ],
45654       [
45655         "Moldova (Republica Moldova)",
45656         "md",
45657         "373"
45658       ],
45659       [
45660         "Monaco",
45661         "mc",
45662         "377"
45663       ],
45664       [
45665         "Mongolia (Монгол)",
45666         "mn",
45667         "976"
45668       ],
45669       [
45670         "Montenegro (Crna Gora)",
45671         "me",
45672         "382"
45673       ],
45674       [
45675         "Montserrat",
45676         "ms",
45677         "1664"
45678       ],
45679       [
45680         "Morocco (‫المغرب‬‎)",
45681         "ma",
45682         "212",
45683         0
45684       ],
45685       [
45686         "Mozambique (Moçambique)",
45687         "mz",
45688         "258"
45689       ],
45690       [
45691         "Myanmar (Burma) (မြန်မာ)",
45692         "mm",
45693         "95"
45694       ],
45695       [
45696         "Namibia (Namibië)",
45697         "na",
45698         "264"
45699       ],
45700       [
45701         "Nauru",
45702         "nr",
45703         "674"
45704       ],
45705       [
45706         "Nepal (नेपाल)",
45707         "np",
45708         "977"
45709       ],
45710       [
45711         "Netherlands (Nederland)",
45712         "nl",
45713         "31"
45714       ],
45715       [
45716         "New Caledonia (Nouvelle-Calédonie)",
45717         "nc",
45718         "687"
45719       ],
45720       [
45721         "New Zealand",
45722         "nz",
45723         "64"
45724       ],
45725       [
45726         "Nicaragua",
45727         "ni",
45728         "505"
45729       ],
45730       [
45731         "Niger (Nijar)",
45732         "ne",
45733         "227"
45734       ],
45735       [
45736         "Nigeria",
45737         "ng",
45738         "234"
45739       ],
45740       [
45741         "Niue",
45742         "nu",
45743         "683"
45744       ],
45745       [
45746         "Norfolk Island",
45747         "nf",
45748         "672"
45749       ],
45750       [
45751         "North Korea (조선 민주주의 인민 공화국)",
45752         "kp",
45753         "850"
45754       ],
45755       [
45756         "Northern Mariana Islands",
45757         "mp",
45758         "1670"
45759       ],
45760       [
45761         "Norway (Norge)",
45762         "no",
45763         "47",
45764         0
45765       ],
45766       [
45767         "Oman (‫عُمان‬‎)",
45768         "om",
45769         "968"
45770       ],
45771       [
45772         "Pakistan (‫پاکستان‬‎)",
45773         "pk",
45774         "92"
45775       ],
45776       [
45777         "Palau",
45778         "pw",
45779         "680"
45780       ],
45781       [
45782         "Palestine (‫فلسطين‬‎)",
45783         "ps",
45784         "970"
45785       ],
45786       [
45787         "Panama (Panamá)",
45788         "pa",
45789         "507"
45790       ],
45791       [
45792         "Papua New Guinea",
45793         "pg",
45794         "675"
45795       ],
45796       [
45797         "Paraguay",
45798         "py",
45799         "595"
45800       ],
45801       [
45802         "Peru (Perú)",
45803         "pe",
45804         "51"
45805       ],
45806       [
45807         "Philippines",
45808         "ph",
45809         "63"
45810       ],
45811       [
45812         "Poland (Polska)",
45813         "pl",
45814         "48"
45815       ],
45816       [
45817         "Portugal",
45818         "pt",
45819         "351"
45820       ],
45821       [
45822         "Puerto Rico",
45823         "pr",
45824         "1",
45825         3,
45826         ["787", "939"]
45827       ],
45828       [
45829         "Qatar (‫قطر‬‎)",
45830         "qa",
45831         "974"
45832       ],
45833       [
45834         "Réunion (La Réunion)",
45835         "re",
45836         "262",
45837         0
45838       ],
45839       [
45840         "Romania (România)",
45841         "ro",
45842         "40"
45843       ],
45844       [
45845         "Russia (Россия)",
45846         "ru",
45847         "7",
45848         0
45849       ],
45850       [
45851         "Rwanda",
45852         "rw",
45853         "250"
45854       ],
45855       [
45856         "Saint Barthélemy",
45857         "bl",
45858         "590",
45859         1
45860       ],
45861       [
45862         "Saint Helena",
45863         "sh",
45864         "290"
45865       ],
45866       [
45867         "Saint Kitts and Nevis",
45868         "kn",
45869         "1869"
45870       ],
45871       [
45872         "Saint Lucia",
45873         "lc",
45874         "1758"
45875       ],
45876       [
45877         "Saint Martin (Saint-Martin (partie française))",
45878         "mf",
45879         "590",
45880         2
45881       ],
45882       [
45883         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
45884         "pm",
45885         "508"
45886       ],
45887       [
45888         "Saint Vincent and the Grenadines",
45889         "vc",
45890         "1784"
45891       ],
45892       [
45893         "Samoa",
45894         "ws",
45895         "685"
45896       ],
45897       [
45898         "San Marino",
45899         "sm",
45900         "378"
45901       ],
45902       [
45903         "São Tomé and Príncipe (São Tomé e Príncipe)",
45904         "st",
45905         "239"
45906       ],
45907       [
45908         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
45909         "sa",
45910         "966"
45911       ],
45912       [
45913         "Senegal (Sénégal)",
45914         "sn",
45915         "221"
45916       ],
45917       [
45918         "Serbia (Србија)",
45919         "rs",
45920         "381"
45921       ],
45922       [
45923         "Seychelles",
45924         "sc",
45925         "248"
45926       ],
45927       [
45928         "Sierra Leone",
45929         "sl",
45930         "232"
45931       ],
45932       [
45933         "Singapore",
45934         "sg",
45935         "65"
45936       ],
45937       [
45938         "Sint Maarten",
45939         "sx",
45940         "1721"
45941       ],
45942       [
45943         "Slovakia (Slovensko)",
45944         "sk",
45945         "421"
45946       ],
45947       [
45948         "Slovenia (Slovenija)",
45949         "si",
45950         "386"
45951       ],
45952       [
45953         "Solomon Islands",
45954         "sb",
45955         "677"
45956       ],
45957       [
45958         "Somalia (Soomaaliya)",
45959         "so",
45960         "252"
45961       ],
45962       [
45963         "South Africa",
45964         "za",
45965         "27"
45966       ],
45967       [
45968         "South Korea (대한민국)",
45969         "kr",
45970         "82"
45971       ],
45972       [
45973         "South Sudan (‫جنوب السودان‬‎)",
45974         "ss",
45975         "211"
45976       ],
45977       [
45978         "Spain (España)",
45979         "es",
45980         "34"
45981       ],
45982       [
45983         "Sri Lanka (ශ්‍රී ලංකාව)",
45984         "lk",
45985         "94"
45986       ],
45987       [
45988         "Sudan (‫السودان‬‎)",
45989         "sd",
45990         "249"
45991       ],
45992       [
45993         "Suriname",
45994         "sr",
45995         "597"
45996       ],
45997       [
45998         "Svalbard and Jan Mayen",
45999         "sj",
46000         "47",
46001         1
46002       ],
46003       [
46004         "Swaziland",
46005         "sz",
46006         "268"
46007       ],
46008       [
46009         "Sweden (Sverige)",
46010         "se",
46011         "46"
46012       ],
46013       [
46014         "Switzerland (Schweiz)",
46015         "ch",
46016         "41"
46017       ],
46018       [
46019         "Syria (‫سوريا‬‎)",
46020         "sy",
46021         "963"
46022       ],
46023       [
46024         "Taiwan (台灣)",
46025         "tw",
46026         "886"
46027       ],
46028       [
46029         "Tajikistan",
46030         "tj",
46031         "992"
46032       ],
46033       [
46034         "Tanzania",
46035         "tz",
46036         "255"
46037       ],
46038       [
46039         "Thailand (ไทย)",
46040         "th",
46041         "66"
46042       ],
46043       [
46044         "Timor-Leste",
46045         "tl",
46046         "670"
46047       ],
46048       [
46049         "Togo",
46050         "tg",
46051         "228"
46052       ],
46053       [
46054         "Tokelau",
46055         "tk",
46056         "690"
46057       ],
46058       [
46059         "Tonga",
46060         "to",
46061         "676"
46062       ],
46063       [
46064         "Trinidad and Tobago",
46065         "tt",
46066         "1868"
46067       ],
46068       [
46069         "Tunisia (‫تونس‬‎)",
46070         "tn",
46071         "216"
46072       ],
46073       [
46074         "Turkey (Türkiye)",
46075         "tr",
46076         "90"
46077       ],
46078       [
46079         "Turkmenistan",
46080         "tm",
46081         "993"
46082       ],
46083       [
46084         "Turks and Caicos Islands",
46085         "tc",
46086         "1649"
46087       ],
46088       [
46089         "Tuvalu",
46090         "tv",
46091         "688"
46092       ],
46093       [
46094         "U.S. Virgin Islands",
46095         "vi",
46096         "1340"
46097       ],
46098       [
46099         "Uganda",
46100         "ug",
46101         "256"
46102       ],
46103       [
46104         "Ukraine (Україна)",
46105         "ua",
46106         "380"
46107       ],
46108       [
46109         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
46110         "ae",
46111         "971"
46112       ],
46113       [
46114         "United Kingdom",
46115         "gb",
46116         "44",
46117         0
46118       ],
46119       [
46120         "United States",
46121         "us",
46122         "1",
46123         0
46124       ],
46125       [
46126         "Uruguay",
46127         "uy",
46128         "598"
46129       ],
46130       [
46131         "Uzbekistan (Oʻzbekiston)",
46132         "uz",
46133         "998"
46134       ],
46135       [
46136         "Vanuatu",
46137         "vu",
46138         "678"
46139       ],
46140       [
46141         "Vatican City (Città del Vaticano)",
46142         "va",
46143         "39",
46144         1
46145       ],
46146       [
46147         "Venezuela",
46148         "ve",
46149         "58"
46150       ],
46151       [
46152         "Vietnam (Việt Nam)",
46153         "vn",
46154         "84"
46155       ],
46156       [
46157         "Wallis and Futuna (Wallis-et-Futuna)",
46158         "wf",
46159         "681"
46160       ],
46161       [
46162         "Western Sahara (‫الصحراء الغربية‬‎)",
46163         "eh",
46164         "212",
46165         1
46166       ],
46167       [
46168         "Yemen (‫اليمن‬‎)",
46169         "ye",
46170         "967"
46171       ],
46172       [
46173         "Zambia",
46174         "zm",
46175         "260"
46176       ],
46177       [
46178         "Zimbabwe",
46179         "zw",
46180         "263"
46181       ],
46182       [
46183         "Åland Islands",
46184         "ax",
46185         "358",
46186         1
46187       ]
46188   ];
46189   
46190   return d;
46191 }/**
46192 *    This script refer to:
46193 *    Title: International Telephone Input
46194 *    Author: Jack O'Connor
46195 *    Code version:  v12.1.12
46196 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46197 **/
46198
46199 /**
46200  * @class Roo.bootstrap.form.PhoneInput
46201  * @extends Roo.bootstrap.form.TriggerField
46202  * An input with International dial-code selection
46203  
46204  * @cfg {String} defaultDialCode default '+852'
46205  * @cfg {Array} preferedCountries default []
46206   
46207  * @constructor
46208  * Create a new PhoneInput.
46209  * @param {Object} config Configuration options
46210  */
46211
46212 Roo.bootstrap.form.PhoneInput = function(config) {
46213     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
46214 };
46215
46216 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
46217         /**
46218         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
46219         */
46220         listWidth: undefined,
46221         
46222         selectedClass: 'active',
46223         
46224         invalidClass : "has-warning",
46225         
46226         validClass: 'has-success',
46227         
46228         allowed: '0123456789',
46229         
46230         max_length: 15,
46231         
46232         /**
46233          * @cfg {String} defaultDialCode The default dial code when initializing the input
46234          */
46235         defaultDialCode: '+852',
46236         
46237         /**
46238          * @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
46239          */
46240         preferedCountries: false,
46241         
46242         getAutoCreate : function()
46243         {
46244             var data = Roo.bootstrap.form.PhoneInputData();
46245             var align = this.labelAlign || this.parentLabelAlign();
46246             var id = Roo.id();
46247             
46248             this.allCountries = [];
46249             this.dialCodeMapping = [];
46250             
46251             for (var i = 0; i < data.length; i++) {
46252               var c = data[i];
46253               this.allCountries[i] = {
46254                 name: c[0],
46255                 iso2: c[1],
46256                 dialCode: c[2],
46257                 priority: c[3] || 0,
46258                 areaCodes: c[4] || null
46259               };
46260               this.dialCodeMapping[c[2]] = {
46261                   name: c[0],
46262                   iso2: c[1],
46263                   priority: c[3] || 0,
46264                   areaCodes: c[4] || null
46265               };
46266             }
46267             
46268             var cfg = {
46269                 cls: 'form-group',
46270                 cn: []
46271             };
46272             
46273             var input =  {
46274                 tag: 'input',
46275                 id : id,
46276                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
46277                 maxlength: this.max_length,
46278                 cls : 'form-control tel-input',
46279                 autocomplete: 'new-password'
46280             };
46281             
46282             var hiddenInput = {
46283                 tag: 'input',
46284                 type: 'hidden',
46285                 cls: 'hidden-tel-input'
46286             };
46287             
46288             if (this.name) {
46289                 hiddenInput.name = this.name;
46290             }
46291             
46292             if (this.disabled) {
46293                 input.disabled = true;
46294             }
46295             
46296             var flag_container = {
46297                 tag: 'div',
46298                 cls: 'flag-box',
46299                 cn: [
46300                     {
46301                         tag: 'div',
46302                         cls: 'flag'
46303                     },
46304                     {
46305                         tag: 'div',
46306                         cls: 'caret'
46307                     }
46308                 ]
46309             };
46310             
46311             var box = {
46312                 tag: 'div',
46313                 cls: this.hasFeedback ? 'has-feedback' : '',
46314                 cn: [
46315                     hiddenInput,
46316                     input,
46317                     {
46318                         tag: 'input',
46319                         cls: 'dial-code-holder',
46320                         disabled: true
46321                     }
46322                 ]
46323             };
46324             
46325             var container = {
46326                 cls: 'roo-select2-container input-group',
46327                 cn: [
46328                     flag_container,
46329                     box
46330                 ]
46331             };
46332             
46333             if (this.fieldLabel.length) {
46334                 var indicator = {
46335                     tag: 'i',
46336                     tooltip: 'This field is required'
46337                 };
46338                 
46339                 var label = {
46340                     tag: 'label',
46341                     'for':  id,
46342                     cls: 'control-label',
46343                     cn: []
46344                 };
46345                 
46346                 var label_text = {
46347                     tag: 'span',
46348                     html: this.fieldLabel
46349                 };
46350                 
46351                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46352                 label.cn = [
46353                     indicator,
46354                     label_text
46355                 ];
46356                 
46357                 if(this.indicatorpos == 'right') {
46358                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46359                     label.cn = [
46360                         label_text,
46361                         indicator
46362                     ];
46363                 }
46364                 
46365                 if(align == 'left') {
46366                     container = {
46367                         tag: 'div',
46368                         cn: [
46369                             container
46370                         ]
46371                     };
46372                     
46373                     if(this.labelWidth > 12){
46374                         label.style = "width: " + this.labelWidth + 'px';
46375                     }
46376                     if(this.labelWidth < 13 && this.labelmd == 0){
46377                         this.labelmd = this.labelWidth;
46378                     }
46379                     if(this.labellg > 0){
46380                         label.cls += ' col-lg-' + this.labellg;
46381                         input.cls += ' col-lg-' + (12 - this.labellg);
46382                     }
46383                     if(this.labelmd > 0){
46384                         label.cls += ' col-md-' + this.labelmd;
46385                         container.cls += ' col-md-' + (12 - this.labelmd);
46386                     }
46387                     if(this.labelsm > 0){
46388                         label.cls += ' col-sm-' + this.labelsm;
46389                         container.cls += ' col-sm-' + (12 - this.labelsm);
46390                     }
46391                     if(this.labelxs > 0){
46392                         label.cls += ' col-xs-' + this.labelxs;
46393                         container.cls += ' col-xs-' + (12 - this.labelxs);
46394                     }
46395                 }
46396             }
46397             
46398             cfg.cn = [
46399                 label,
46400                 container
46401             ];
46402             
46403             var settings = this;
46404             
46405             ['xs','sm','md','lg'].map(function(size){
46406                 if (settings[size]) {
46407                     cfg.cls += ' col-' + size + '-' + settings[size];
46408                 }
46409             });
46410             
46411             this.store = new Roo.data.Store({
46412                 proxy : new Roo.data.MemoryProxy({}),
46413                 reader : new Roo.data.JsonReader({
46414                     fields : [
46415                         {
46416                             'name' : 'name',
46417                             'type' : 'string'
46418                         },
46419                         {
46420                             'name' : 'iso2',
46421                             'type' : 'string'
46422                         },
46423                         {
46424                             'name' : 'dialCode',
46425                             'type' : 'string'
46426                         },
46427                         {
46428                             'name' : 'priority',
46429                             'type' : 'string'
46430                         },
46431                         {
46432                             'name' : 'areaCodes',
46433                             'type' : 'string'
46434                         }
46435                     ]
46436                 })
46437             });
46438             
46439             if(!this.preferedCountries) {
46440                 this.preferedCountries = [
46441                     'hk',
46442                     'gb',
46443                     'us'
46444                 ];
46445             }
46446             
46447             var p = this.preferedCountries.reverse();
46448             
46449             if(p) {
46450                 for (var i = 0; i < p.length; i++) {
46451                     for (var j = 0; j < this.allCountries.length; j++) {
46452                         if(this.allCountries[j].iso2 == p[i]) {
46453                             var t = this.allCountries[j];
46454                             this.allCountries.splice(j,1);
46455                             this.allCountries.unshift(t);
46456                         }
46457                     } 
46458                 }
46459             }
46460             
46461             this.store.proxy.data = {
46462                 success: true,
46463                 data: this.allCountries
46464             };
46465             
46466             return cfg;
46467         },
46468         
46469         initEvents : function()
46470         {
46471             this.createList();
46472             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
46473             
46474             this.indicator = this.indicatorEl();
46475             this.flag = this.flagEl();
46476             this.dialCodeHolder = this.dialCodeHolderEl();
46477             
46478             this.trigger = this.el.select('div.flag-box',true).first();
46479             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
46480             
46481             var _this = this;
46482             
46483             (function(){
46484                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46485                 _this.list.setWidth(lw);
46486             }).defer(100);
46487             
46488             this.list.on('mouseover', this.onViewOver, this);
46489             this.list.on('mousemove', this.onViewMove, this);
46490             this.inputEl().on("keyup", this.onKeyUp, this);
46491             this.inputEl().on("keypress", this.onKeyPress, this);
46492             
46493             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
46494
46495             this.view = new Roo.View(this.list, this.tpl, {
46496                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
46497             });
46498             
46499             this.view.on('click', this.onViewClick, this);
46500             this.setValue(this.defaultDialCode);
46501         },
46502         
46503         onTriggerClick : function(e)
46504         {
46505             Roo.log('trigger click');
46506             if(this.disabled){
46507                 return;
46508             }
46509             
46510             if(this.isExpanded()){
46511                 this.collapse();
46512                 this.hasFocus = false;
46513             }else {
46514                 this.store.load({});
46515                 this.hasFocus = true;
46516                 this.expand();
46517             }
46518         },
46519         
46520         isExpanded : function()
46521         {
46522             return this.list.isVisible();
46523         },
46524         
46525         collapse : function()
46526         {
46527             if(!this.isExpanded()){
46528                 return;
46529             }
46530             this.list.hide();
46531             Roo.get(document).un('mousedown', this.collapseIf, this);
46532             Roo.get(document).un('mousewheel', this.collapseIf, this);
46533             this.fireEvent('collapse', this);
46534             this.validate();
46535         },
46536         
46537         expand : function()
46538         {
46539             Roo.log('expand');
46540
46541             if(this.isExpanded() || !this.hasFocus){
46542                 return;
46543             }
46544             
46545             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
46546             this.list.setWidth(lw);
46547             
46548             this.list.show();
46549             this.restrictHeight();
46550             
46551             Roo.get(document).on('mousedown', this.collapseIf, this);
46552             Roo.get(document).on('mousewheel', this.collapseIf, this);
46553             
46554             this.fireEvent('expand', this);
46555         },
46556         
46557         restrictHeight : function()
46558         {
46559             this.list.alignTo(this.inputEl(), this.listAlign);
46560             this.list.alignTo(this.inputEl(), this.listAlign);
46561         },
46562         
46563         onViewOver : function(e, t)
46564         {
46565             if(this.inKeyMode){
46566                 return;
46567             }
46568             var item = this.view.findItemFromChild(t);
46569             
46570             if(item){
46571                 var index = this.view.indexOf(item);
46572                 this.select(index, false);
46573             }
46574         },
46575
46576         // private
46577         onViewClick : function(view, doFocus, el, e)
46578         {
46579             var index = this.view.getSelectedIndexes()[0];
46580             
46581             var r = this.store.getAt(index);
46582             
46583             if(r){
46584                 this.onSelect(r, index);
46585             }
46586             if(doFocus !== false && !this.blockFocus){
46587                 this.inputEl().focus();
46588             }
46589         },
46590         
46591         onViewMove : function(e, t)
46592         {
46593             this.inKeyMode = false;
46594         },
46595         
46596         select : function(index, scrollIntoView)
46597         {
46598             this.selectedIndex = index;
46599             this.view.select(index);
46600             if(scrollIntoView !== false){
46601                 var el = this.view.getNode(index);
46602                 if(el){
46603                     this.list.scrollChildIntoView(el, false);
46604                 }
46605             }
46606         },
46607         
46608         createList : function()
46609         {
46610             this.list = Roo.get(document.body).createChild({
46611                 tag: 'ul',
46612                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
46613                 style: 'display:none'
46614             });
46615             
46616             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
46617         },
46618         
46619         collapseIf : function(e)
46620         {
46621             var in_combo  = e.within(this.el);
46622             var in_list =  e.within(this.list);
46623             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
46624             
46625             if (in_combo || in_list || is_list) {
46626                 return;
46627             }
46628             this.collapse();
46629         },
46630         
46631         onSelect : function(record, index)
46632         {
46633             if(this.fireEvent('beforeselect', this, record, index) !== false){
46634                 
46635                 this.setFlagClass(record.data.iso2);
46636                 this.setDialCode(record.data.dialCode);
46637                 this.hasFocus = false;
46638                 this.collapse();
46639                 this.fireEvent('select', this, record, index);
46640             }
46641         },
46642         
46643         flagEl : function()
46644         {
46645             var flag = this.el.select('div.flag',true).first();
46646             if(!flag){
46647                 return false;
46648             }
46649             return flag;
46650         },
46651         
46652         dialCodeHolderEl : function()
46653         {
46654             var d = this.el.select('input.dial-code-holder',true).first();
46655             if(!d){
46656                 return false;
46657             }
46658             return d;
46659         },
46660         
46661         setDialCode : function(v)
46662         {
46663             this.dialCodeHolder.dom.value = '+'+v;
46664         },
46665         
46666         setFlagClass : function(n)
46667         {
46668             this.flag.dom.className = 'flag '+n;
46669         },
46670         
46671         getValue : function()
46672         {
46673             var v = this.inputEl().getValue();
46674             if(this.dialCodeHolder) {
46675                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
46676             }
46677             return v;
46678         },
46679         
46680         setValue : function(v)
46681         {
46682             var d = this.getDialCode(v);
46683             
46684             //invalid dial code
46685             if(v.length == 0 || !d || d.length == 0) {
46686                 if(this.rendered){
46687                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
46688                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
46689                 }
46690                 return;
46691             }
46692             
46693             //valid dial code
46694             this.setFlagClass(this.dialCodeMapping[d].iso2);
46695             this.setDialCode(d);
46696             this.inputEl().dom.value = v.replace('+'+d,'');
46697             this.hiddenEl().dom.value = this.getValue();
46698             
46699             this.validate();
46700         },
46701         
46702         getDialCode : function(v)
46703         {
46704             v = v ||  '';
46705             
46706             if (v.length == 0) {
46707                 return this.dialCodeHolder.dom.value;
46708             }
46709             
46710             var dialCode = "";
46711             if (v.charAt(0) != "+") {
46712                 return false;
46713             }
46714             var numericChars = "";
46715             for (var i = 1; i < v.length; i++) {
46716               var c = v.charAt(i);
46717               if (!isNaN(c)) {
46718                 numericChars += c;
46719                 if (this.dialCodeMapping[numericChars]) {
46720                   dialCode = v.substr(1, i);
46721                 }
46722                 if (numericChars.length == 4) {
46723                   break;
46724                 }
46725               }
46726             }
46727             return dialCode;
46728         },
46729         
46730         reset : function()
46731         {
46732             this.setValue(this.defaultDialCode);
46733             this.validate();
46734         },
46735         
46736         hiddenEl : function()
46737         {
46738             return this.el.select('input.hidden-tel-input',true).first();
46739         },
46740         
46741         // after setting val
46742         onKeyUp : function(e){
46743             this.setValue(this.getValue());
46744         },
46745         
46746         onKeyPress : function(e){
46747             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
46748                 e.stopEvent();
46749             }
46750         }
46751         
46752 });
46753 /**
46754  * @class Roo.bootstrap.form.MoneyField
46755  * @extends Roo.bootstrap.form.ComboBox
46756  * Bootstrap MoneyField class
46757  * 
46758  * @constructor
46759  * Create a new MoneyField.
46760  * @param {Object} config Configuration options
46761  */
46762
46763 Roo.bootstrap.form.MoneyField = function(config) {
46764     
46765     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
46766     
46767 };
46768
46769 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
46770     
46771     /**
46772      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
46773      */
46774     allowDecimals : true,
46775     /**
46776      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
46777      */
46778     decimalSeparator : ".",
46779     /**
46780      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
46781      */
46782     decimalPrecision : 0,
46783     /**
46784      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
46785      */
46786     allowNegative : true,
46787     /**
46788      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
46789      */
46790     allowZero: true,
46791     /**
46792      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
46793      */
46794     minValue : Number.NEGATIVE_INFINITY,
46795     /**
46796      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
46797      */
46798     maxValue : Number.MAX_VALUE,
46799     /**
46800      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
46801      */
46802     minText : "The minimum value for this field is {0}",
46803     /**
46804      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
46805      */
46806     maxText : "The maximum value for this field is {0}",
46807     /**
46808      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
46809      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
46810      */
46811     nanText : "{0} is not a valid number",
46812     /**
46813      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
46814      */
46815     castInt : true,
46816     /**
46817      * @cfg {String} defaults currency of the MoneyField
46818      * value should be in lkey
46819      */
46820     defaultCurrency : false,
46821     /**
46822      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
46823      */
46824     thousandsDelimiter : false,
46825     /**
46826      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
46827      */
46828     max_length: false,
46829     
46830     inputlg : 9,
46831     inputmd : 9,
46832     inputsm : 9,
46833     inputxs : 6,
46834      /**
46835      * @cfg {Roo.data.Store} store  Store to lookup currency??
46836      */
46837     store : false,
46838     
46839     getAutoCreate : function()
46840     {
46841         var align = this.labelAlign || this.parentLabelAlign();
46842         
46843         var id = Roo.id();
46844
46845         var cfg = {
46846             cls: 'form-group',
46847             cn: []
46848         };
46849
46850         var input =  {
46851             tag: 'input',
46852             id : id,
46853             cls : 'form-control roo-money-amount-input',
46854             autocomplete: 'new-password'
46855         };
46856         
46857         var hiddenInput = {
46858             tag: 'input',
46859             type: 'hidden',
46860             id: Roo.id(),
46861             cls: 'hidden-number-input'
46862         };
46863         
46864         if(this.max_length) {
46865             input.maxlength = this.max_length; 
46866         }
46867         
46868         if (this.name) {
46869             hiddenInput.name = this.name;
46870         }
46871
46872         if (this.disabled) {
46873             input.disabled = true;
46874         }
46875
46876         var clg = 12 - this.inputlg;
46877         var cmd = 12 - this.inputmd;
46878         var csm = 12 - this.inputsm;
46879         var cxs = 12 - this.inputxs;
46880         
46881         var container = {
46882             tag : 'div',
46883             cls : 'row roo-money-field',
46884             cn : [
46885                 {
46886                     tag : 'div',
46887                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
46888                     cn : [
46889                         {
46890                             tag : 'div',
46891                             cls: 'roo-select2-container input-group',
46892                             cn: [
46893                                 {
46894                                     tag : 'input',
46895                                     cls : 'form-control roo-money-currency-input',
46896                                     autocomplete: 'new-password',
46897                                     readOnly : 1,
46898                                     name : this.currencyName
46899                                 },
46900                                 {
46901                                     tag :'span',
46902                                     cls : 'input-group-addon',
46903                                     cn : [
46904                                         {
46905                                             tag: 'span',
46906                                             cls: 'caret'
46907                                         }
46908                                     ]
46909                                 }
46910                             ]
46911                         }
46912                     ]
46913                 },
46914                 {
46915                     tag : 'div',
46916                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
46917                     cn : [
46918                         {
46919                             tag: 'div',
46920                             cls: this.hasFeedback ? 'has-feedback' : '',
46921                             cn: [
46922                                 input
46923                             ]
46924                         }
46925                     ]
46926                 }
46927             ]
46928             
46929         };
46930         
46931         if (this.fieldLabel.length) {
46932             var indicator = {
46933                 tag: 'i',
46934                 tooltip: 'This field is required'
46935             };
46936
46937             var label = {
46938                 tag: 'label',
46939                 'for':  id,
46940                 cls: 'control-label',
46941                 cn: []
46942             };
46943
46944             var label_text = {
46945                 tag: 'span',
46946                 html: this.fieldLabel
46947             };
46948
46949             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46950             label.cn = [
46951                 indicator,
46952                 label_text
46953             ];
46954
46955             if(this.indicatorpos == 'right') {
46956                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46957                 label.cn = [
46958                     label_text,
46959                     indicator
46960                 ];
46961             }
46962
46963             if(align == 'left') {
46964                 container = {
46965                     tag: 'div',
46966                     cn: [
46967                         container
46968                     ]
46969                 };
46970
46971                 if(this.labelWidth > 12){
46972                     label.style = "width: " + this.labelWidth + 'px';
46973                 }
46974                 if(this.labelWidth < 13 && this.labelmd == 0){
46975                     this.labelmd = this.labelWidth;
46976                 }
46977                 if(this.labellg > 0){
46978                     label.cls += ' col-lg-' + this.labellg;
46979                     input.cls += ' col-lg-' + (12 - this.labellg);
46980                 }
46981                 if(this.labelmd > 0){
46982                     label.cls += ' col-md-' + this.labelmd;
46983                     container.cls += ' col-md-' + (12 - this.labelmd);
46984                 }
46985                 if(this.labelsm > 0){
46986                     label.cls += ' col-sm-' + this.labelsm;
46987                     container.cls += ' col-sm-' + (12 - this.labelsm);
46988                 }
46989                 if(this.labelxs > 0){
46990                     label.cls += ' col-xs-' + this.labelxs;
46991                     container.cls += ' col-xs-' + (12 - this.labelxs);
46992                 }
46993             }
46994         }
46995
46996         cfg.cn = [
46997             label,
46998             container,
46999             hiddenInput
47000         ];
47001         
47002         var settings = this;
47003
47004         ['xs','sm','md','lg'].map(function(size){
47005             if (settings[size]) {
47006                 cfg.cls += ' col-' + size + '-' + settings[size];
47007             }
47008         });
47009         
47010         return cfg;
47011     },
47012     
47013     initEvents : function()
47014     {
47015         this.indicator = this.indicatorEl();
47016         
47017         this.initCurrencyEvent();
47018         
47019         this.initNumberEvent();
47020     },
47021     
47022     initCurrencyEvent : function()
47023     {
47024         if (!this.store) {
47025             throw "can not find store for combo";
47026         }
47027         
47028         this.store = Roo.factory(this.store, Roo.data);
47029         this.store.parent = this;
47030         
47031         this.createList();
47032         
47033         this.triggerEl = this.el.select('.input-group-addon', true).first();
47034         
47035         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
47036         
47037         var _this = this;
47038         
47039         (function(){
47040             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47041             _this.list.setWidth(lw);
47042         }).defer(100);
47043         
47044         this.list.on('mouseover', this.onViewOver, this);
47045         this.list.on('mousemove', this.onViewMove, this);
47046         this.list.on('scroll', this.onViewScroll, this);
47047         
47048         if(!this.tpl){
47049             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
47050         }
47051         
47052         this.view = new Roo.View(this.list, this.tpl, {
47053             singleSelect:true, store: this.store, selectedClass: this.selectedClass
47054         });
47055         
47056         this.view.on('click', this.onViewClick, this);
47057         
47058         this.store.on('beforeload', this.onBeforeLoad, this);
47059         this.store.on('load', this.onLoad, this);
47060         this.store.on('loadexception', this.onLoadException, this);
47061         
47062         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
47063             "up" : function(e){
47064                 this.inKeyMode = true;
47065                 this.selectPrev();
47066             },
47067
47068             "down" : function(e){
47069                 if(!this.isExpanded()){
47070                     this.onTriggerClick();
47071                 }else{
47072                     this.inKeyMode = true;
47073                     this.selectNext();
47074                 }
47075             },
47076
47077             "enter" : function(e){
47078                 this.collapse();
47079                 
47080                 if(this.fireEvent("specialkey", this, e)){
47081                     this.onViewClick(false);
47082                 }
47083                 
47084                 return true;
47085             },
47086
47087             "esc" : function(e){
47088                 this.collapse();
47089             },
47090
47091             "tab" : function(e){
47092                 this.collapse();
47093                 
47094                 if(this.fireEvent("specialkey", this, e)){
47095                     this.onViewClick(false);
47096                 }
47097                 
47098                 return true;
47099             },
47100
47101             scope : this,
47102
47103             doRelay : function(foo, bar, hname){
47104                 if(hname == 'down' || this.scope.isExpanded()){
47105                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
47106                 }
47107                 return true;
47108             },
47109
47110             forceKeyDown: true
47111         });
47112         
47113         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
47114         
47115     },
47116     
47117     initNumberEvent : function(e)
47118     {
47119         this.inputEl().on("keydown" , this.fireKey,  this);
47120         this.inputEl().on("focus", this.onFocus,  this);
47121         this.inputEl().on("blur", this.onBlur,  this);
47122         
47123         this.inputEl().relayEvent('keyup', this);
47124         
47125         if(this.indicator){
47126             this.indicator.addClass('invisible');
47127         }
47128  
47129         this.originalValue = this.getValue();
47130         
47131         if(this.validationEvent == 'keyup'){
47132             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
47133             this.inputEl().on('keyup', this.filterValidation, this);
47134         }
47135         else if(this.validationEvent !== false){
47136             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
47137         }
47138         
47139         if(this.selectOnFocus){
47140             this.on("focus", this.preFocus, this);
47141             
47142         }
47143         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
47144             this.inputEl().on("keypress", this.filterKeys, this);
47145         } else {
47146             this.inputEl().relayEvent('keypress', this);
47147         }
47148         
47149         var allowed = "0123456789";
47150         
47151         if(this.allowDecimals){
47152             allowed += this.decimalSeparator;
47153         }
47154         
47155         if(this.allowNegative){
47156             allowed += "-";
47157         }
47158         
47159         if(this.thousandsDelimiter) {
47160             allowed += ",";
47161         }
47162         
47163         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
47164         
47165         var keyPress = function(e){
47166             
47167             var k = e.getKey();
47168             
47169             var c = e.getCharCode();
47170             
47171             if(
47172                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
47173                     allowed.indexOf(String.fromCharCode(c)) === -1
47174             ){
47175                 e.stopEvent();
47176                 return;
47177             }
47178             
47179             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
47180                 return;
47181             }
47182             
47183             if(allowed.indexOf(String.fromCharCode(c)) === -1){
47184                 e.stopEvent();
47185             }
47186         };
47187         
47188         this.inputEl().on("keypress", keyPress, this);
47189         
47190     },
47191     
47192     onTriggerClick : function(e)
47193     {   
47194         if(this.disabled){
47195             return;
47196         }
47197         
47198         this.page = 0;
47199         this.loadNext = false;
47200         
47201         if(this.isExpanded()){
47202             this.collapse();
47203             return;
47204         }
47205         
47206         this.hasFocus = true;
47207         
47208         if(this.triggerAction == 'all') {
47209             this.doQuery(this.allQuery, true);
47210             return;
47211         }
47212         
47213         this.doQuery(this.getRawValue());
47214     },
47215     
47216     getCurrency : function()
47217     {   
47218         var v = this.currencyEl().getValue();
47219         
47220         return v;
47221     },
47222     
47223     restrictHeight : function()
47224     {
47225         this.list.alignTo(this.currencyEl(), this.listAlign);
47226         this.list.alignTo(this.currencyEl(), this.listAlign);
47227     },
47228     
47229     onViewClick : function(view, doFocus, el, e)
47230     {
47231         var index = this.view.getSelectedIndexes()[0];
47232         
47233         var r = this.store.getAt(index);
47234         
47235         if(r){
47236             this.onSelect(r, index);
47237         }
47238     },
47239     
47240     onSelect : function(record, index){
47241         
47242         if(this.fireEvent('beforeselect', this, record, index) !== false){
47243         
47244             this.setFromCurrencyData(index > -1 ? record.data : false);
47245             
47246             this.collapse();
47247             
47248             this.fireEvent('select', this, record, index);
47249         }
47250     },
47251     
47252     setFromCurrencyData : function(o)
47253     {
47254         var currency = '';
47255         
47256         this.lastCurrency = o;
47257         
47258         if (this.currencyField) {
47259             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
47260         } else {
47261             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
47262         }
47263         
47264         this.lastSelectionText = currency;
47265         
47266         //setting default currency
47267         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
47268             this.setCurrency(this.defaultCurrency);
47269             return;
47270         }
47271         
47272         this.setCurrency(currency);
47273     },
47274     
47275     setFromData : function(o)
47276     {
47277         var c = {};
47278         
47279         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
47280         
47281         this.setFromCurrencyData(c);
47282         
47283         var value = '';
47284         
47285         if (this.name) {
47286             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
47287         } else {
47288             Roo.log('no value set for '+ (this.name ? this.name : this.id));
47289         }
47290         
47291         this.setValue(value);
47292         
47293     },
47294     
47295     setCurrency : function(v)
47296     {   
47297         this.currencyValue = v;
47298         
47299         if(this.rendered){
47300             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
47301             this.validate();
47302         }
47303     },
47304     
47305     setValue : function(v)
47306     {
47307         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
47308         
47309         this.value = v;
47310         
47311         if(this.rendered){
47312             
47313             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47314             
47315             this.inputEl().dom.value = (v == '') ? '' :
47316                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
47317             
47318             if(!this.allowZero && v === '0') {
47319                 this.hiddenEl().dom.value = '';
47320                 this.inputEl().dom.value = '';
47321             }
47322             
47323             this.validate();
47324         }
47325     },
47326     
47327     getRawValue : function()
47328     {
47329         var v = this.inputEl().getValue();
47330         
47331         return v;
47332     },
47333     
47334     getValue : function()
47335     {
47336         return this.fixPrecision(this.parseValue(this.getRawValue()));
47337     },
47338     
47339     parseValue : function(value)
47340     {
47341         if(this.thousandsDelimiter) {
47342             value += "";
47343             r = new RegExp(",", "g");
47344             value = value.replace(r, "");
47345         }
47346         
47347         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
47348         return isNaN(value) ? '' : value;
47349         
47350     },
47351     
47352     fixPrecision : function(value)
47353     {
47354         if(this.thousandsDelimiter) {
47355             value += "";
47356             r = new RegExp(",", "g");
47357             value = value.replace(r, "");
47358         }
47359         
47360         var nan = isNaN(value);
47361         
47362         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
47363             return nan ? '' : value;
47364         }
47365         return parseFloat(value).toFixed(this.decimalPrecision);
47366     },
47367     
47368     decimalPrecisionFcn : function(v)
47369     {
47370         return Math.floor(v);
47371     },
47372     
47373     validateValue : function(value)
47374     {
47375         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
47376             return false;
47377         }
47378         
47379         var num = this.parseValue(value);
47380         
47381         if(isNaN(num)){
47382             this.markInvalid(String.format(this.nanText, value));
47383             return false;
47384         }
47385         
47386         if(num < this.minValue){
47387             this.markInvalid(String.format(this.minText, this.minValue));
47388             return false;
47389         }
47390         
47391         if(num > this.maxValue){
47392             this.markInvalid(String.format(this.maxText, this.maxValue));
47393             return false;
47394         }
47395         
47396         return true;
47397     },
47398     
47399     validate : function()
47400     {
47401         if(this.disabled || this.allowBlank){
47402             this.markValid();
47403             return true;
47404         }
47405         
47406         var currency = this.getCurrency();
47407         
47408         if(this.validateValue(this.getRawValue()) && currency.length){
47409             this.markValid();
47410             return true;
47411         }
47412         
47413         this.markInvalid();
47414         return false;
47415     },
47416     
47417     getName: function()
47418     {
47419         return this.name;
47420     },
47421     
47422     beforeBlur : function()
47423     {
47424         if(!this.castInt){
47425             return;
47426         }
47427         
47428         var v = this.parseValue(this.getRawValue());
47429         
47430         if(v || v == 0){
47431             this.setValue(v);
47432         }
47433     },
47434     
47435     onBlur : function()
47436     {
47437         this.beforeBlur();
47438         
47439         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
47440             //this.el.removeClass(this.focusClass);
47441         }
47442         
47443         this.hasFocus = false;
47444         
47445         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
47446             this.validate();
47447         }
47448         
47449         var v = this.getValue();
47450         
47451         if(String(v) !== String(this.startValue)){
47452             this.fireEvent('change', this, v, this.startValue);
47453         }
47454         
47455         this.fireEvent("blur", this);
47456     },
47457     
47458     inputEl : function()
47459     {
47460         return this.el.select('.roo-money-amount-input', true).first();
47461     },
47462     
47463     currencyEl : function()
47464     {
47465         return this.el.select('.roo-money-currency-input', true).first();
47466     },
47467     
47468     hiddenEl : function()
47469     {
47470         return this.el.select('input.hidden-number-input',true).first();
47471     }
47472     
47473 });/**
47474  * @class Roo.bootstrap.BezierSignature
47475  * @extends Roo.bootstrap.Component
47476  * Bootstrap BezierSignature class
47477  * This script refer to:
47478  *    Title: Signature Pad
47479  *    Author: szimek
47480  *    Availability: https://github.com/szimek/signature_pad
47481  *
47482  * @constructor
47483  * Create a new BezierSignature
47484  * @param {Object} config The config object
47485  */
47486
47487 Roo.bootstrap.BezierSignature = function(config){
47488     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
47489     this.addEvents({
47490         "resize" : true
47491     });
47492 };
47493
47494 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
47495 {
47496      
47497     curve_data: [],
47498     
47499     is_empty: true,
47500     
47501     mouse_btn_down: true,
47502     
47503     /**
47504      * @cfg {int} canvas height
47505      */
47506     canvas_height: '200px',
47507     
47508     /**
47509      * @cfg {float|function} Radius of a single dot.
47510      */ 
47511     dot_size: false,
47512     
47513     /**
47514      * @cfg {float} Minimum width of a line. Defaults to 0.5.
47515      */
47516     min_width: 0.5,
47517     
47518     /**
47519      * @cfg {float} Maximum width of a line. Defaults to 2.5.
47520      */
47521     max_width: 2.5,
47522     
47523     /**
47524      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
47525      */
47526     throttle: 16,
47527     
47528     /**
47529      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
47530      */
47531     min_distance: 5,
47532     
47533     /**
47534      * @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.
47535      */
47536     bg_color: 'rgba(0, 0, 0, 0)',
47537     
47538     /**
47539      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
47540      */
47541     dot_color: 'black',
47542     
47543     /**
47544      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
47545      */ 
47546     velocity_filter_weight: 0.7,
47547     
47548     /**
47549      * @cfg {function} Callback when stroke begin. 
47550      */
47551     onBegin: false,
47552     
47553     /**
47554      * @cfg {function} Callback when stroke end.
47555      */
47556     onEnd: false,
47557     
47558     getAutoCreate : function()
47559     {
47560         var cls = 'roo-signature column';
47561         
47562         if(this.cls){
47563             cls += ' ' + this.cls;
47564         }
47565         
47566         var col_sizes = [
47567             'lg',
47568             'md',
47569             'sm',
47570             'xs'
47571         ];
47572         
47573         for(var i = 0; i < col_sizes.length; i++) {
47574             if(this[col_sizes[i]]) {
47575                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
47576             }
47577         }
47578         
47579         var cfg = {
47580             tag: 'div',
47581             cls: cls,
47582             cn: [
47583                 {
47584                     tag: 'div',
47585                     cls: 'roo-signature-body',
47586                     cn: [
47587                         {
47588                             tag: 'canvas',
47589                             cls: 'roo-signature-body-canvas',
47590                             height: this.canvas_height,
47591                             width: this.canvas_width
47592                         }
47593                     ]
47594                 },
47595                 {
47596                     tag: 'input',
47597                     type: 'file',
47598                     style: 'display: none'
47599                 }
47600             ]
47601         };
47602         
47603         return cfg;
47604     },
47605     
47606     initEvents: function() 
47607     {
47608         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
47609         
47610         var canvas = this.canvasEl();
47611         
47612         // mouse && touch event swapping...
47613         canvas.dom.style.touchAction = 'none';
47614         canvas.dom.style.msTouchAction = 'none';
47615         
47616         this.mouse_btn_down = false;
47617         canvas.on('mousedown', this._handleMouseDown, this);
47618         canvas.on('mousemove', this._handleMouseMove, this);
47619         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
47620         
47621         if (window.PointerEvent) {
47622             canvas.on('pointerdown', this._handleMouseDown, this);
47623             canvas.on('pointermove', this._handleMouseMove, this);
47624             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
47625         }
47626         
47627         if ('ontouchstart' in window) {
47628             canvas.on('touchstart', this._handleTouchStart, this);
47629             canvas.on('touchmove', this._handleTouchMove, this);
47630             canvas.on('touchend', this._handleTouchEnd, this);
47631         }
47632         
47633         Roo.EventManager.onWindowResize(this.resize, this, true);
47634         
47635         // file input event
47636         this.fileEl().on('change', this.uploadImage, this);
47637         
47638         this.clear();
47639         
47640         this.resize();
47641     },
47642     
47643     resize: function(){
47644         
47645         var canvas = this.canvasEl().dom;
47646         var ctx = this.canvasElCtx();
47647         var img_data = false;
47648         
47649         if(canvas.width > 0) {
47650             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
47651         }
47652         // setting canvas width will clean img data
47653         canvas.width = 0;
47654         
47655         var style = window.getComputedStyle ? 
47656             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
47657             
47658         var padding_left = parseInt(style.paddingLeft) || 0;
47659         var padding_right = parseInt(style.paddingRight) || 0;
47660         
47661         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
47662         
47663         if(img_data) {
47664             ctx.putImageData(img_data, 0, 0);
47665         }
47666     },
47667     
47668     _handleMouseDown: function(e)
47669     {
47670         if (e.browserEvent.which === 1) {
47671             this.mouse_btn_down = true;
47672             this.strokeBegin(e);
47673         }
47674     },
47675     
47676     _handleMouseMove: function (e)
47677     {
47678         if (this.mouse_btn_down) {
47679             this.strokeMoveUpdate(e);
47680         }
47681     },
47682     
47683     _handleMouseUp: function (e)
47684     {
47685         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
47686             this.mouse_btn_down = false;
47687             this.strokeEnd(e);
47688         }
47689     },
47690     
47691     _handleTouchStart: function (e) {
47692         
47693         e.preventDefault();
47694         if (e.browserEvent.targetTouches.length === 1) {
47695             // var touch = e.browserEvent.changedTouches[0];
47696             // this.strokeBegin(touch);
47697             
47698              this.strokeBegin(e); // assume e catching the correct xy...
47699         }
47700     },
47701     
47702     _handleTouchMove: function (e) {
47703         e.preventDefault();
47704         // var touch = event.targetTouches[0];
47705         // _this._strokeMoveUpdate(touch);
47706         this.strokeMoveUpdate(e);
47707     },
47708     
47709     _handleTouchEnd: function (e) {
47710         var wasCanvasTouched = e.target === this.canvasEl().dom;
47711         if (wasCanvasTouched) {
47712             e.preventDefault();
47713             // var touch = event.changedTouches[0];
47714             // _this._strokeEnd(touch);
47715             this.strokeEnd(e);
47716         }
47717     },
47718     
47719     reset: function () {
47720         this._lastPoints = [];
47721         this._lastVelocity = 0;
47722         this._lastWidth = (this.min_width + this.max_width) / 2;
47723         this.canvasElCtx().fillStyle = this.dot_color;
47724     },
47725     
47726     strokeMoveUpdate: function(e)
47727     {
47728         this.strokeUpdate(e);
47729         
47730         if (this.throttle) {
47731             this.throttleStroke(this.strokeUpdate, this.throttle);
47732         }
47733         else {
47734             this.strokeUpdate(e);
47735         }
47736     },
47737     
47738     strokeBegin: function(e)
47739     {
47740         var newPointGroup = {
47741             color: this.dot_color,
47742             points: []
47743         };
47744         
47745         if (typeof this.onBegin === 'function') {
47746             this.onBegin(e);
47747         }
47748         
47749         this.curve_data.push(newPointGroup);
47750         this.reset();
47751         this.strokeUpdate(e);
47752     },
47753     
47754     strokeUpdate: function(e)
47755     {
47756         var rect = this.canvasEl().dom.getBoundingClientRect();
47757         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
47758         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
47759         var lastPoints = lastPointGroup.points;
47760         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
47761         var isLastPointTooClose = lastPoint
47762             ? point.distanceTo(lastPoint) <= this.min_distance
47763             : false;
47764         var color = lastPointGroup.color;
47765         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
47766             var curve = this.addPoint(point);
47767             if (!lastPoint) {
47768                 this.drawDot({color: color, point: point});
47769             }
47770             else if (curve) {
47771                 this.drawCurve({color: color, curve: curve});
47772             }
47773             lastPoints.push({
47774                 time: point.time,
47775                 x: point.x,
47776                 y: point.y
47777             });
47778         }
47779     },
47780     
47781     strokeEnd: function(e)
47782     {
47783         this.strokeUpdate(e);
47784         if (typeof this.onEnd === 'function') {
47785             this.onEnd(e);
47786         }
47787     },
47788     
47789     addPoint:  function (point) {
47790         var _lastPoints = this._lastPoints;
47791         _lastPoints.push(point);
47792         if (_lastPoints.length > 2) {
47793             if (_lastPoints.length === 3) {
47794                 _lastPoints.unshift(_lastPoints[0]);
47795             }
47796             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
47797             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
47798             _lastPoints.shift();
47799             return curve;
47800         }
47801         return null;
47802     },
47803     
47804     calculateCurveWidths: function (startPoint, endPoint) {
47805         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
47806             (1 - this.velocity_filter_weight) * this._lastVelocity;
47807
47808         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
47809         var widths = {
47810             end: newWidth,
47811             start: this._lastWidth
47812         };
47813         
47814         this._lastVelocity = velocity;
47815         this._lastWidth = newWidth;
47816         return widths;
47817     },
47818     
47819     drawDot: function (_a) {
47820         var color = _a.color, point = _a.point;
47821         var ctx = this.canvasElCtx();
47822         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
47823         ctx.beginPath();
47824         this.drawCurveSegment(point.x, point.y, width);
47825         ctx.closePath();
47826         ctx.fillStyle = color;
47827         ctx.fill();
47828     },
47829     
47830     drawCurve: function (_a) {
47831         var color = _a.color, curve = _a.curve;
47832         var ctx = this.canvasElCtx();
47833         var widthDelta = curve.endWidth - curve.startWidth;
47834         var drawSteps = Math.floor(curve.length()) * 2;
47835         ctx.beginPath();
47836         ctx.fillStyle = color;
47837         for (var i = 0; i < drawSteps; i += 1) {
47838         var t = i / drawSteps;
47839         var tt = t * t;
47840         var ttt = tt * t;
47841         var u = 1 - t;
47842         var uu = u * u;
47843         var uuu = uu * u;
47844         var x = uuu * curve.startPoint.x;
47845         x += 3 * uu * t * curve.control1.x;
47846         x += 3 * u * tt * curve.control2.x;
47847         x += ttt * curve.endPoint.x;
47848         var y = uuu * curve.startPoint.y;
47849         y += 3 * uu * t * curve.control1.y;
47850         y += 3 * u * tt * curve.control2.y;
47851         y += ttt * curve.endPoint.y;
47852         var width = curve.startWidth + ttt * widthDelta;
47853         this.drawCurveSegment(x, y, width);
47854         }
47855         ctx.closePath();
47856         ctx.fill();
47857     },
47858     
47859     drawCurveSegment: function (x, y, width) {
47860         var ctx = this.canvasElCtx();
47861         ctx.moveTo(x, y);
47862         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
47863         this.is_empty = false;
47864     },
47865     
47866     clear: function()
47867     {
47868         var ctx = this.canvasElCtx();
47869         var canvas = this.canvasEl().dom;
47870         ctx.fillStyle = this.bg_color;
47871         ctx.clearRect(0, 0, canvas.width, canvas.height);
47872         ctx.fillRect(0, 0, canvas.width, canvas.height);
47873         this.curve_data = [];
47874         this.reset();
47875         this.is_empty = true;
47876     },
47877     
47878     fileEl: function()
47879     {
47880         return  this.el.select('input',true).first();
47881     },
47882     
47883     canvasEl: function()
47884     {
47885         return this.el.select('canvas',true).first();
47886     },
47887     
47888     canvasElCtx: function()
47889     {
47890         return this.el.select('canvas',true).first().dom.getContext('2d');
47891     },
47892     
47893     getImage: function(type)
47894     {
47895         if(this.is_empty) {
47896             return false;
47897         }
47898         
47899         // encryption ?
47900         return this.canvasEl().dom.toDataURL('image/'+type, 1);
47901     },
47902     
47903     drawFromImage: function(img_src)
47904     {
47905         var img = new Image();
47906         
47907         img.onload = function(){
47908             this.canvasElCtx().drawImage(img, 0, 0);
47909         }.bind(this);
47910         
47911         img.src = img_src;
47912         
47913         this.is_empty = false;
47914     },
47915     
47916     selectImage: function()
47917     {
47918         this.fileEl().dom.click();
47919     },
47920     
47921     uploadImage: function(e)
47922     {
47923         var reader = new FileReader();
47924         
47925         reader.onload = function(e){
47926             var img = new Image();
47927             img.onload = function(){
47928                 this.reset();
47929                 this.canvasElCtx().drawImage(img, 0, 0);
47930             }.bind(this);
47931             img.src = e.target.result;
47932         }.bind(this);
47933         
47934         reader.readAsDataURL(e.target.files[0]);
47935     },
47936     
47937     // Bezier Point Constructor
47938     Point: (function () {
47939         function Point(x, y, time) {
47940             this.x = x;
47941             this.y = y;
47942             this.time = time || Date.now();
47943         }
47944         Point.prototype.distanceTo = function (start) {
47945             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
47946         };
47947         Point.prototype.equals = function (other) {
47948             return this.x === other.x && this.y === other.y && this.time === other.time;
47949         };
47950         Point.prototype.velocityFrom = function (start) {
47951             return this.time !== start.time
47952             ? this.distanceTo(start) / (this.time - start.time)
47953             : 0;
47954         };
47955         return Point;
47956     }()),
47957     
47958     
47959     // Bezier Constructor
47960     Bezier: (function () {
47961         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
47962             this.startPoint = startPoint;
47963             this.control2 = control2;
47964             this.control1 = control1;
47965             this.endPoint = endPoint;
47966             this.startWidth = startWidth;
47967             this.endWidth = endWidth;
47968         }
47969         Bezier.fromPoints = function (points, widths, scope) {
47970             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
47971             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
47972             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
47973         };
47974         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
47975             var dx1 = s1.x - s2.x;
47976             var dy1 = s1.y - s2.y;
47977             var dx2 = s2.x - s3.x;
47978             var dy2 = s2.y - s3.y;
47979             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
47980             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
47981             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
47982             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
47983             var dxm = m1.x - m2.x;
47984             var dym = m1.y - m2.y;
47985             var k = l2 / (l1 + l2);
47986             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
47987             var tx = s2.x - cm.x;
47988             var ty = s2.y - cm.y;
47989             return {
47990                 c1: new scope.Point(m1.x + tx, m1.y + ty),
47991                 c2: new scope.Point(m2.x + tx, m2.y + ty)
47992             };
47993         };
47994         Bezier.prototype.length = function () {
47995             var steps = 10;
47996             var length = 0;
47997             var px;
47998             var py;
47999             for (var i = 0; i <= steps; i += 1) {
48000                 var t = i / steps;
48001                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
48002                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
48003                 if (i > 0) {
48004                     var xdiff = cx - px;
48005                     var ydiff = cy - py;
48006                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
48007                 }
48008                 px = cx;
48009                 py = cy;
48010             }
48011             return length;
48012         };
48013         Bezier.prototype.point = function (t, start, c1, c2, end) {
48014             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
48015             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
48016             + (3.0 * c2 * (1.0 - t) * t * t)
48017             + (end * t * t * t);
48018         };
48019         return Bezier;
48020     }()),
48021     
48022     throttleStroke: function(fn, wait) {
48023       if (wait === void 0) { wait = 250; }
48024       var previous = 0;
48025       var timeout = null;
48026       var result;
48027       var storedContext;
48028       var storedArgs;
48029       var later = function () {
48030           previous = Date.now();
48031           timeout = null;
48032           result = fn.apply(storedContext, storedArgs);
48033           if (!timeout) {
48034               storedContext = null;
48035               storedArgs = [];
48036           }
48037       };
48038       return function wrapper() {
48039           var args = [];
48040           for (var _i = 0; _i < arguments.length; _i++) {
48041               args[_i] = arguments[_i];
48042           }
48043           var now = Date.now();
48044           var remaining = wait - (now - previous);
48045           storedContext = this;
48046           storedArgs = args;
48047           if (remaining <= 0 || remaining > wait) {
48048               if (timeout) {
48049                   clearTimeout(timeout);
48050                   timeout = null;
48051               }
48052               previous = now;
48053               result = fn.apply(storedContext, storedArgs);
48054               if (!timeout) {
48055                   storedContext = null;
48056                   storedArgs = [];
48057               }
48058           }
48059           else if (!timeout) {
48060               timeout = window.setTimeout(later, remaining);
48061           }
48062           return result;
48063       };
48064   }
48065   
48066 });
48067
48068  
48069
48070  // old names for form elements
48071 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
48072 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
48073 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
48074 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
48075 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
48076 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
48077 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
48078 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
48079 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
48080 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
48081 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
48082 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
48083 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
48084 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
48085 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
48086 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
48087 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
48088 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
48089 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
48090 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
48091 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
48092 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
48093 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
48094 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
48095 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
48096 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
48097
48098 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
48099 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
48100
48101 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
48102 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
48103
48104 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
48105 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
48106 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
48107 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
48108