roojs-bootstrap.js
[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         Roo.log("Tooltip enter");
32572         Roo.log(el);
32573         Roo.log(dom);
32574         if (this.currentEl) {
32575             //Roo.log(dom);
32576             //Roo.log(this.currentEl);
32577             //Roo.log(this.currentEl.contains(dom));
32578             if (this.currentEl == el) {
32579                 return;
32580             }
32581             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
32582                 return;
32583             }
32584
32585         }
32586         
32587         if (this.currentTip.el) {
32588             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
32589         }    
32590         //Roo.log(ev);
32591         
32592         if(!el || el.dom == document){
32593             return;
32594         }
32595         
32596         var bindEl = el; 
32597         var pel = false;
32598         if (!el.attr('tooltip')) {
32599             pel = el.findParent("[tooltip]");
32600             if (pel) {
32601                 bindEl = Roo.get(pel);
32602             }
32603         }
32604         
32605        
32606         
32607         // you can not look for children, as if el is the body.. then everythign is the child..
32608         if (!pel && !el.attr('tooltip')) { //
32609             if (!el.select("[tooltip]").elements.length) {
32610                 return;
32611             }
32612             // is the mouse over this child...?
32613             bindEl = el.select("[tooltip]").first();
32614             var xy = ev.getXY();
32615             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
32616                 //Roo.log("not in region.");
32617                 return;
32618             }
32619             //Roo.log("child element over..");
32620             
32621         }
32622         this.currentEl = el;
32623         this.currentTip.bind(bindEl);
32624         this.currentRegion = Roo.lib.Region.getRegion(dom);
32625         this.currentTip.enter();
32626         
32627     },
32628     leave : function(ev)
32629     {
32630         var dom = ev.getTarget();
32631         //Roo.log(['leave',dom]);
32632         if (!this.currentEl) {
32633             return;
32634         }
32635         
32636         
32637         if (dom != this.currentEl.dom) {
32638             return;
32639         }
32640         var xy = ev.getXY();
32641         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
32642             return;
32643         }
32644         // only activate leave if mouse cursor is outside... bounding box..
32645         
32646         
32647         
32648         
32649         if (this.currentTip) {
32650             this.currentTip.leave();
32651         }
32652         //Roo.log('clear currentEl');
32653         this.currentEl = false;
32654         
32655         
32656     },
32657     alignment : {
32658         'left' : ['r-l', [-2,0], 'right'],
32659         'right' : ['l-r', [2,0], 'left'],
32660         'bottom' : ['t-b', [0,2], 'top'],
32661         'top' : [ 'b-t', [0,-2], 'bottom']
32662     }
32663     
32664 });
32665
32666
32667 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
32668     
32669     
32670     bindEl : false,
32671     
32672     delay : null, // can be { show : 300 , hide: 500}
32673     
32674     timeout : null,
32675     
32676     hoverState : null, //???
32677     
32678     placement : 'bottom', 
32679     
32680     alignment : false,
32681     
32682     getAutoCreate : function(){
32683     
32684         var cfg = {
32685            cls : 'tooltip',   
32686            role : 'tooltip',
32687            cn : [
32688                 {
32689                     cls : 'tooltip-arrow arrow'
32690                 },
32691                 {
32692                     cls : 'tooltip-inner'
32693                 }
32694            ]
32695         };
32696         
32697         return cfg;
32698     },
32699     bind : function(el)
32700     {
32701         this.bindEl = el;
32702     },
32703     
32704     initEvents : function()
32705     {
32706         this.arrowEl = this.el.select('.arrow', true).first();
32707         this.innerEl = this.el.select('.tooltip-inner', true).first();
32708     },
32709     
32710     enter : function () {
32711        
32712         if (this.timeout != null) {
32713             clearTimeout(this.timeout);
32714         }
32715         
32716         this.hoverState = 'in';
32717          //Roo.log("enter - show");
32718         if (!this.delay || !this.delay.show) {
32719             this.show();
32720             return;
32721         }
32722         var _t = this;
32723         this.timeout = setTimeout(function () {
32724             if (_t.hoverState == 'in') {
32725                 _t.show();
32726             }
32727         }, this.delay.show);
32728     },
32729     leave : function()
32730     {
32731         clearTimeout(this.timeout);
32732     
32733         this.hoverState = 'out';
32734          if (!this.delay || !this.delay.hide) {
32735             this.hide();
32736             return;
32737         }
32738        
32739         var _t = this;
32740         this.timeout = setTimeout(function () {
32741             //Roo.log("leave - timeout");
32742             
32743             if (_t.hoverState == 'out') {
32744                 _t.hide();
32745                 Roo.bootstrap.Tooltip.currentEl = false;
32746             }
32747         }, delay);
32748     },
32749     
32750     show : function (msg)
32751     {
32752         if (!this.el) {
32753             this.render(document.body);
32754         }
32755         // set content.
32756         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
32757         
32758         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
32759         
32760         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
32761         
32762         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
32763                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
32764         
32765         var placement = typeof this.placement == 'function' ?
32766             this.placement.call(this, this.el, on_el) :
32767             this.placement;
32768             
32769         var autoToken = /\s?auto?\s?/i;
32770         var autoPlace = autoToken.test(placement);
32771         if (autoPlace) {
32772             placement = placement.replace(autoToken, '') || 'top';
32773         }
32774         
32775         //this.el.detach()
32776         //this.el.setXY([0,0]);
32777         this.el.show();
32778         //this.el.dom.style.display='block';
32779         
32780         //this.el.appendTo(on_el);
32781         
32782         var p = this.getPosition();
32783         var box = this.el.getBox();
32784         
32785         if (autoPlace) {
32786             // fixme..
32787         }
32788         
32789         var align = this.alignment[placement];
32790         
32791         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
32792         
32793         if(placement == 'top' || placement == 'bottom'){
32794             if(xy[0] < 0){
32795                 placement = 'right';
32796             }
32797             
32798             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
32799                 placement = 'left';
32800             }
32801             
32802             var scroll = Roo.select('body', true).first().getScroll();
32803             
32804             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
32805                 placement = 'top';
32806             }
32807             
32808             align = this.alignment[placement];
32809             
32810             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
32811             
32812         }
32813         
32814         var elems = document.getElementsByTagName('div');
32815         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
32816         for (var i = 0; i < elems.length; i++) {
32817           var zindex = Number.parseInt(
32818                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
32819                 10
32820           );
32821           if (zindex > highest) {
32822             highest = zindex;
32823           }
32824         }
32825         
32826         
32827         
32828         this.el.dom.style.zIndex = highest;
32829         
32830         this.el.alignTo(this.bindEl, align[0],align[1]);
32831         //var arrow = this.el.select('.arrow',true).first();
32832         //arrow.set(align[2], 
32833         
32834         this.el.addClass(placement);
32835         this.el.addClass("bs-tooltip-"+ placement);
32836         
32837         this.el.addClass('in fade show');
32838         
32839         this.hoverState = null;
32840         
32841         if (this.el.hasClass('fade')) {
32842             // fade it?
32843         }
32844         
32845         
32846         
32847         
32848         
32849     },
32850     hide : function()
32851     {
32852          
32853         if (!this.el) {
32854             return;
32855         }
32856         //this.el.setXY([0,0]);
32857         this.el.removeClass(['show', 'in']);
32858         //this.el.hide();
32859         
32860     }
32861     
32862 });
32863  
32864
32865  /*
32866  * - LGPL
32867  *
32868  * Location Picker
32869  * 
32870  */
32871
32872 /**
32873  * @class Roo.bootstrap.LocationPicker
32874  * @extends Roo.bootstrap.Component
32875  * Bootstrap LocationPicker class
32876  * @cfg {Number} latitude Position when init default 0
32877  * @cfg {Number} longitude Position when init default 0
32878  * @cfg {Number} zoom default 15
32879  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
32880  * @cfg {Boolean} mapTypeControl default false
32881  * @cfg {Boolean} disableDoubleClickZoom default false
32882  * @cfg {Boolean} scrollwheel default true
32883  * @cfg {Boolean} streetViewControl default false
32884  * @cfg {Number} radius default 0
32885  * @cfg {String} locationName
32886  * @cfg {Boolean} draggable default true
32887  * @cfg {Boolean} enableAutocomplete default false
32888  * @cfg {Boolean} enableReverseGeocode default true
32889  * @cfg {String} markerTitle
32890  * 
32891  * @constructor
32892  * Create a new LocationPicker
32893  * @param {Object} config The config object
32894  */
32895
32896
32897 Roo.bootstrap.LocationPicker = function(config){
32898     
32899     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
32900     
32901     this.addEvents({
32902         /**
32903          * @event initial
32904          * Fires when the picker initialized.
32905          * @param {Roo.bootstrap.LocationPicker} this
32906          * @param {Google Location} location
32907          */
32908         initial : true,
32909         /**
32910          * @event positionchanged
32911          * Fires when the picker position changed.
32912          * @param {Roo.bootstrap.LocationPicker} this
32913          * @param {Google Location} location
32914          */
32915         positionchanged : true,
32916         /**
32917          * @event resize
32918          * Fires when the map resize.
32919          * @param {Roo.bootstrap.LocationPicker} this
32920          */
32921         resize : true,
32922         /**
32923          * @event show
32924          * Fires when the map show.
32925          * @param {Roo.bootstrap.LocationPicker} this
32926          */
32927         show : true,
32928         /**
32929          * @event hide
32930          * Fires when the map hide.
32931          * @param {Roo.bootstrap.LocationPicker} this
32932          */
32933         hide : true,
32934         /**
32935          * @event mapClick
32936          * Fires when click the map.
32937          * @param {Roo.bootstrap.LocationPicker} this
32938          * @param {Map event} e
32939          */
32940         mapClick : true,
32941         /**
32942          * @event mapRightClick
32943          * Fires when right click the map.
32944          * @param {Roo.bootstrap.LocationPicker} this
32945          * @param {Map event} e
32946          */
32947         mapRightClick : true,
32948         /**
32949          * @event markerClick
32950          * Fires when click the marker.
32951          * @param {Roo.bootstrap.LocationPicker} this
32952          * @param {Map event} e
32953          */
32954         markerClick : true,
32955         /**
32956          * @event markerRightClick
32957          * Fires when right click the marker.
32958          * @param {Roo.bootstrap.LocationPicker} this
32959          * @param {Map event} e
32960          */
32961         markerRightClick : true,
32962         /**
32963          * @event OverlayViewDraw
32964          * Fires when OverlayView Draw
32965          * @param {Roo.bootstrap.LocationPicker} this
32966          */
32967         OverlayViewDraw : true,
32968         /**
32969          * @event OverlayViewOnAdd
32970          * Fires when OverlayView Draw
32971          * @param {Roo.bootstrap.LocationPicker} this
32972          */
32973         OverlayViewOnAdd : true,
32974         /**
32975          * @event OverlayViewOnRemove
32976          * Fires when OverlayView Draw
32977          * @param {Roo.bootstrap.LocationPicker} this
32978          */
32979         OverlayViewOnRemove : true,
32980         /**
32981          * @event OverlayViewShow
32982          * Fires when OverlayView Draw
32983          * @param {Roo.bootstrap.LocationPicker} this
32984          * @param {Pixel} cpx
32985          */
32986         OverlayViewShow : true,
32987         /**
32988          * @event OverlayViewHide
32989          * Fires when OverlayView Draw
32990          * @param {Roo.bootstrap.LocationPicker} this
32991          */
32992         OverlayViewHide : true,
32993         /**
32994          * @event loadexception
32995          * Fires when load google lib failed.
32996          * @param {Roo.bootstrap.LocationPicker} this
32997          */
32998         loadexception : true
32999     });
33000         
33001 };
33002
33003 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
33004     
33005     gMapContext: false,
33006     
33007     latitude: 0,
33008     longitude: 0,
33009     zoom: 15,
33010     mapTypeId: false,
33011     mapTypeControl: false,
33012     disableDoubleClickZoom: false,
33013     scrollwheel: true,
33014     streetViewControl: false,
33015     radius: 0,
33016     locationName: '',
33017     draggable: true,
33018     enableAutocomplete: false,
33019     enableReverseGeocode: true,
33020     markerTitle: '',
33021     
33022     getAutoCreate: function()
33023     {
33024
33025         var cfg = {
33026             tag: 'div',
33027             cls: 'roo-location-picker'
33028         };
33029         
33030         return cfg
33031     },
33032     
33033     initEvents: function(ct, position)
33034     {       
33035         if(!this.el.getWidth() || this.isApplied()){
33036             return;
33037         }
33038         
33039         this.el.setVisibilityMode(Roo.Element.DISPLAY);
33040         
33041         this.initial();
33042     },
33043     
33044     initial: function()
33045     {
33046         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
33047             this.fireEvent('loadexception', this);
33048             return;
33049         }
33050         
33051         if(!this.mapTypeId){
33052             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
33053         }
33054         
33055         this.gMapContext = this.GMapContext();
33056         
33057         this.initOverlayView();
33058         
33059         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
33060         
33061         var _this = this;
33062                 
33063         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
33064             _this.setPosition(_this.gMapContext.marker.position);
33065         });
33066         
33067         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
33068             _this.fireEvent('mapClick', this, event);
33069             
33070         });
33071
33072         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
33073             _this.fireEvent('mapRightClick', this, event);
33074             
33075         });
33076         
33077         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
33078             _this.fireEvent('markerClick', this, event);
33079             
33080         });
33081
33082         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
33083             _this.fireEvent('markerRightClick', this, event);
33084             
33085         });
33086         
33087         this.setPosition(this.gMapContext.location);
33088         
33089         this.fireEvent('initial', this, this.gMapContext.location);
33090     },
33091     
33092     initOverlayView: function()
33093     {
33094         var _this = this;
33095         
33096         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
33097             
33098             draw: function()
33099             {
33100                 _this.fireEvent('OverlayViewDraw', _this);
33101             },
33102             
33103             onAdd: function()
33104             {
33105                 _this.fireEvent('OverlayViewOnAdd', _this);
33106             },
33107             
33108             onRemove: function()
33109             {
33110                 _this.fireEvent('OverlayViewOnRemove', _this);
33111             },
33112             
33113             show: function(cpx)
33114             {
33115                 _this.fireEvent('OverlayViewShow', _this, cpx);
33116             },
33117             
33118             hide: function()
33119             {
33120                 _this.fireEvent('OverlayViewHide', _this);
33121             }
33122             
33123         });
33124     },
33125     
33126     fromLatLngToContainerPixel: function(event)
33127     {
33128         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
33129     },
33130     
33131     isApplied: function() 
33132     {
33133         return this.getGmapContext() == false ? false : true;
33134     },
33135     
33136     getGmapContext: function() 
33137     {
33138         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
33139     },
33140     
33141     GMapContext: function() 
33142     {
33143         var position = new google.maps.LatLng(this.latitude, this.longitude);
33144         
33145         var _map = new google.maps.Map(this.el.dom, {
33146             center: position,
33147             zoom: this.zoom,
33148             mapTypeId: this.mapTypeId,
33149             mapTypeControl: this.mapTypeControl,
33150             disableDoubleClickZoom: this.disableDoubleClickZoom,
33151             scrollwheel: this.scrollwheel,
33152             streetViewControl: this.streetViewControl,
33153             locationName: this.locationName,
33154             draggable: this.draggable,
33155             enableAutocomplete: this.enableAutocomplete,
33156             enableReverseGeocode: this.enableReverseGeocode
33157         });
33158         
33159         var _marker = new google.maps.Marker({
33160             position: position,
33161             map: _map,
33162             title: this.markerTitle,
33163             draggable: this.draggable
33164         });
33165         
33166         return {
33167             map: _map,
33168             marker: _marker,
33169             circle: null,
33170             location: position,
33171             radius: this.radius,
33172             locationName: this.locationName,
33173             addressComponents: {
33174                 formatted_address: null,
33175                 addressLine1: null,
33176                 addressLine2: null,
33177                 streetName: null,
33178                 streetNumber: null,
33179                 city: null,
33180                 district: null,
33181                 state: null,
33182                 stateOrProvince: null
33183             },
33184             settings: this,
33185             domContainer: this.el.dom,
33186             geodecoder: new google.maps.Geocoder()
33187         };
33188     },
33189     
33190     drawCircle: function(center, radius, options) 
33191     {
33192         if (this.gMapContext.circle != null) {
33193             this.gMapContext.circle.setMap(null);
33194         }
33195         if (radius > 0) {
33196             radius *= 1;
33197             options = Roo.apply({}, options, {
33198                 strokeColor: "#0000FF",
33199                 strokeOpacity: .35,
33200                 strokeWeight: 2,
33201                 fillColor: "#0000FF",
33202                 fillOpacity: .2
33203             });
33204             
33205             options.map = this.gMapContext.map;
33206             options.radius = radius;
33207             options.center = center;
33208             this.gMapContext.circle = new google.maps.Circle(options);
33209             return this.gMapContext.circle;
33210         }
33211         
33212         return null;
33213     },
33214     
33215     setPosition: function(location) 
33216     {
33217         this.gMapContext.location = location;
33218         this.gMapContext.marker.setPosition(location);
33219         this.gMapContext.map.panTo(location);
33220         this.drawCircle(location, this.gMapContext.radius, {});
33221         
33222         var _this = this;
33223         
33224         if (this.gMapContext.settings.enableReverseGeocode) {
33225             this.gMapContext.geodecoder.geocode({
33226                 latLng: this.gMapContext.location
33227             }, function(results, status) {
33228                 
33229                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
33230                     _this.gMapContext.locationName = results[0].formatted_address;
33231                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
33232                     
33233                     _this.fireEvent('positionchanged', this, location);
33234                 }
33235             });
33236             
33237             return;
33238         }
33239         
33240         this.fireEvent('positionchanged', this, location);
33241     },
33242     
33243     resize: function()
33244     {
33245         google.maps.event.trigger(this.gMapContext.map, "resize");
33246         
33247         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
33248         
33249         this.fireEvent('resize', this);
33250     },
33251     
33252     setPositionByLatLng: function(latitude, longitude)
33253     {
33254         this.setPosition(new google.maps.LatLng(latitude, longitude));
33255     },
33256     
33257     getCurrentPosition: function() 
33258     {
33259         return {
33260             latitude: this.gMapContext.location.lat(),
33261             longitude: this.gMapContext.location.lng()
33262         };
33263     },
33264     
33265     getAddressName: function() 
33266     {
33267         return this.gMapContext.locationName;
33268     },
33269     
33270     getAddressComponents: function() 
33271     {
33272         return this.gMapContext.addressComponents;
33273     },
33274     
33275     address_component_from_google_geocode: function(address_components) 
33276     {
33277         var result = {};
33278         
33279         for (var i = 0; i < address_components.length; i++) {
33280             var component = address_components[i];
33281             if (component.types.indexOf("postal_code") >= 0) {
33282                 result.postalCode = component.short_name;
33283             } else if (component.types.indexOf("street_number") >= 0) {
33284                 result.streetNumber = component.short_name;
33285             } else if (component.types.indexOf("route") >= 0) {
33286                 result.streetName = component.short_name;
33287             } else if (component.types.indexOf("neighborhood") >= 0) {
33288                 result.city = component.short_name;
33289             } else if (component.types.indexOf("locality") >= 0) {
33290                 result.city = component.short_name;
33291             } else if (component.types.indexOf("sublocality") >= 0) {
33292                 result.district = component.short_name;
33293             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
33294                 result.stateOrProvince = component.short_name;
33295             } else if (component.types.indexOf("country") >= 0) {
33296                 result.country = component.short_name;
33297             }
33298         }
33299         
33300         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
33301         result.addressLine2 = "";
33302         return result;
33303     },
33304     
33305     setZoomLevel: function(zoom)
33306     {
33307         this.gMapContext.map.setZoom(zoom);
33308     },
33309     
33310     show: function()
33311     {
33312         if(!this.el){
33313             return;
33314         }
33315         
33316         this.el.show();
33317         
33318         this.resize();
33319         
33320         this.fireEvent('show', this);
33321     },
33322     
33323     hide: function()
33324     {
33325         if(!this.el){
33326             return;
33327         }
33328         
33329         this.el.hide();
33330         
33331         this.fireEvent('hide', this);
33332     }
33333     
33334 });
33335
33336 Roo.apply(Roo.bootstrap.LocationPicker, {
33337     
33338     OverlayView : function(map, options)
33339     {
33340         options = options || {};
33341         
33342         this.setMap(map);
33343     }
33344     
33345     
33346 });/**
33347  * @class Roo.bootstrap.Alert
33348  * @extends Roo.bootstrap.Component
33349  * Bootstrap Alert class - shows an alert area box
33350  * eg
33351  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
33352   Enter a valid email address
33353 </div>
33354  * @licence LGPL
33355  * @cfg {String} title The title of alert
33356  * @cfg {String} html The content of alert
33357  * @cfg {String} weight (success|info|warning|danger) Weight of the message
33358  * @cfg {String} fa font-awesomeicon
33359  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
33360  * @cfg {Boolean} close true to show a x closer
33361  * 
33362  * 
33363  * @constructor
33364  * Create a new alert
33365  * @param {Object} config The config object
33366  */
33367
33368
33369 Roo.bootstrap.Alert = function(config){
33370     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
33371     
33372 };
33373
33374 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
33375     
33376     title: '',
33377     html: '',
33378     weight: false,
33379     fa: false,
33380     faicon: false, // BC
33381     close : false,
33382     
33383     
33384     getAutoCreate : function()
33385     {
33386         
33387         var cfg = {
33388             tag : 'div',
33389             cls : 'alert',
33390             cn : [
33391                 {
33392                     tag: 'button',
33393                     type :  "button",
33394                     cls: "close",
33395                     html : '×',
33396                     style : this.close ? '' : 'display:none'
33397                 },
33398                 {
33399                     tag : 'i',
33400                     cls : 'roo-alert-icon'
33401                     
33402                 },
33403                 {
33404                     tag : 'b',
33405                     cls : 'roo-alert-title',
33406                     html : this.title
33407                 },
33408                 {
33409                     tag : 'span',
33410                     cls : 'roo-alert-text',
33411                     html : this.html
33412                 }
33413             ]
33414         };
33415         
33416         if(this.faicon){
33417             cfg.cn[0].cls += ' fa ' + this.faicon;
33418         }
33419         if(this.fa){
33420             cfg.cn[0].cls += ' fa ' + this.fa;
33421         }
33422         
33423         if(this.weight){
33424             cfg.cls += ' alert-' + this.weight;
33425         }
33426         
33427         return cfg;
33428     },
33429     
33430     initEvents: function() 
33431     {
33432         this.el.setVisibilityMode(Roo.Element.DISPLAY);
33433         this.titleEl =  this.el.select('.roo-alert-title',true).first();
33434         this.iconEl = this.el.select('.roo-alert-icon',true).first();
33435         this.htmlEl = this.el.select('.roo-alert-text',true).first();
33436         if (this.seconds > 0) {
33437             this.hide.defer(this.seconds, this);
33438         }
33439     },
33440     /**
33441      * Set the Title Message HTML
33442      * @param {String} html
33443      */
33444     setTitle : function(str)
33445     {
33446         this.titleEl.dom.innerHTML = str;
33447     },
33448      
33449      /**
33450      * Set the Body Message HTML
33451      * @param {String} html
33452      */
33453     setHtml : function(str)
33454     {
33455         this.htmlEl.dom.innerHTML = str;
33456     },
33457     /**
33458      * Set the Weight of the alert
33459      * @param {String} (success|info|warning|danger) weight
33460      */
33461     
33462     setWeight : function(weight)
33463     {
33464         if(this.weight){
33465             this.el.removeClass('alert-' + this.weight);
33466         }
33467         
33468         this.weight = weight;
33469         
33470         this.el.addClass('alert-' + this.weight);
33471     },
33472       /**
33473      * Set the Icon of the alert
33474      * @param {String} see fontawsome names (name without the 'fa-' bit)
33475      */
33476     setIcon : function(icon)
33477     {
33478         if(this.faicon){
33479             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
33480         }
33481         
33482         this.faicon = icon;
33483         
33484         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
33485     },
33486     /**
33487      * Hide the Alert
33488      */
33489     hide: function() 
33490     {
33491         this.el.hide();   
33492     },
33493     /**
33494      * Show the Alert
33495      */
33496     show: function() 
33497     {  
33498         this.el.show();   
33499     }
33500     
33501 });
33502
33503  
33504 /*
33505 * Licence: LGPL
33506 */
33507
33508 /**
33509  * @class Roo.bootstrap.UploadCropbox
33510  * @extends Roo.bootstrap.Component
33511  * Bootstrap UploadCropbox class
33512  * @cfg {String} emptyText show when image has been loaded
33513  * @cfg {String} rotateNotify show when image too small to rotate
33514  * @cfg {Number} errorTimeout default 3000
33515  * @cfg {Number} minWidth default 300
33516  * @cfg {Number} minHeight default 300
33517  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
33518  * @cfg {Boolean} isDocument (true|false) default false
33519  * @cfg {String} url action url
33520  * @cfg {String} paramName default 'imageUpload'
33521  * @cfg {String} method default POST
33522  * @cfg {Boolean} loadMask (true|false) default true
33523  * @cfg {Boolean} loadingText default 'Loading...'
33524  * 
33525  * @constructor
33526  * Create a new UploadCropbox
33527  * @param {Object} config The config object
33528  */
33529
33530 Roo.bootstrap.UploadCropbox = function(config){
33531     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
33532     
33533     this.addEvents({
33534         /**
33535          * @event beforeselectfile
33536          * Fire before select file
33537          * @param {Roo.bootstrap.UploadCropbox} this
33538          */
33539         "beforeselectfile" : true,
33540         /**
33541          * @event initial
33542          * Fire after initEvent
33543          * @param {Roo.bootstrap.UploadCropbox} this
33544          */
33545         "initial" : true,
33546         /**
33547          * @event crop
33548          * Fire after initEvent
33549          * @param {Roo.bootstrap.UploadCropbox} this
33550          * @param {String} data
33551          */
33552         "crop" : true,
33553         /**
33554          * @event prepare
33555          * Fire when preparing the file data
33556          * @param {Roo.bootstrap.UploadCropbox} this
33557          * @param {Object} file
33558          */
33559         "prepare" : true,
33560         /**
33561          * @event exception
33562          * Fire when get exception
33563          * @param {Roo.bootstrap.UploadCropbox} this
33564          * @param {XMLHttpRequest} xhr
33565          */
33566         "exception" : true,
33567         /**
33568          * @event beforeloadcanvas
33569          * Fire before load the canvas
33570          * @param {Roo.bootstrap.UploadCropbox} this
33571          * @param {String} src
33572          */
33573         "beforeloadcanvas" : true,
33574         /**
33575          * @event trash
33576          * Fire when trash image
33577          * @param {Roo.bootstrap.UploadCropbox} this
33578          */
33579         "trash" : true,
33580         /**
33581          * @event download
33582          * Fire when download the image
33583          * @param {Roo.bootstrap.UploadCropbox} this
33584          */
33585         "download" : true,
33586         /**
33587          * @event footerbuttonclick
33588          * Fire when footerbuttonclick
33589          * @param {Roo.bootstrap.UploadCropbox} this
33590          * @param {String} type
33591          */
33592         "footerbuttonclick" : true,
33593         /**
33594          * @event resize
33595          * Fire when resize
33596          * @param {Roo.bootstrap.UploadCropbox} this
33597          */
33598         "resize" : true,
33599         /**
33600          * @event rotate
33601          * Fire when rotate the image
33602          * @param {Roo.bootstrap.UploadCropbox} this
33603          * @param {String} pos
33604          */
33605         "rotate" : true,
33606         /**
33607          * @event inspect
33608          * Fire when inspect the file
33609          * @param {Roo.bootstrap.UploadCropbox} this
33610          * @param {Object} file
33611          */
33612         "inspect" : true,
33613         /**
33614          * @event upload
33615          * Fire when xhr upload the file
33616          * @param {Roo.bootstrap.UploadCropbox} this
33617          * @param {Object} data
33618          */
33619         "upload" : true,
33620         /**
33621          * @event arrange
33622          * Fire when arrange the file data
33623          * @param {Roo.bootstrap.UploadCropbox} this
33624          * @param {Object} formData
33625          */
33626         "arrange" : true
33627     });
33628     
33629     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
33630 };
33631
33632 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
33633     
33634     emptyText : 'Click to upload image',
33635     rotateNotify : 'Image is too small to rotate',
33636     errorTimeout : 3000,
33637     scale : 0,
33638     baseScale : 1,
33639     rotate : 0,
33640     dragable : false,
33641     pinching : false,
33642     mouseX : 0,
33643     mouseY : 0,
33644     cropData : false,
33645     minWidth : 300,
33646     minHeight : 300,
33647     file : false,
33648     exif : {},
33649     baseRotate : 1,
33650     cropType : 'image/jpeg',
33651     buttons : false,
33652     canvasLoaded : false,
33653     isDocument : false,
33654     method : 'POST',
33655     paramName : 'imageUpload',
33656     loadMask : true,
33657     loadingText : 'Loading...',
33658     maskEl : false,
33659     
33660     getAutoCreate : function()
33661     {
33662         var cfg = {
33663             tag : 'div',
33664             cls : 'roo-upload-cropbox',
33665             cn : [
33666                 {
33667                     tag : 'input',
33668                     cls : 'roo-upload-cropbox-selector',
33669                     type : 'file'
33670                 },
33671                 {
33672                     tag : 'div',
33673                     cls : 'roo-upload-cropbox-body',
33674                     style : 'cursor:pointer',
33675                     cn : [
33676                         {
33677                             tag : 'div',
33678                             cls : 'roo-upload-cropbox-preview'
33679                         },
33680                         {
33681                             tag : 'div',
33682                             cls : 'roo-upload-cropbox-thumb'
33683                         },
33684                         {
33685                             tag : 'div',
33686                             cls : 'roo-upload-cropbox-empty-notify',
33687                             html : this.emptyText
33688                         },
33689                         {
33690                             tag : 'div',
33691                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
33692                             html : this.rotateNotify
33693                         }
33694                     ]
33695                 },
33696                 {
33697                     tag : 'div',
33698                     cls : 'roo-upload-cropbox-footer',
33699                     cn : {
33700                         tag : 'div',
33701                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
33702                         cn : []
33703                     }
33704                 }
33705             ]
33706         };
33707         
33708         return cfg;
33709     },
33710     
33711     onRender : function(ct, position)
33712     {
33713         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
33714         
33715         if (this.buttons.length) {
33716             
33717             Roo.each(this.buttons, function(bb) {
33718                 
33719                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
33720                 
33721                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
33722                 
33723             }, this);
33724         }
33725         
33726         if(this.loadMask){
33727             this.maskEl = this.el;
33728         }
33729     },
33730     
33731     initEvents : function()
33732     {
33733         this.urlAPI = (window.createObjectURL && window) || 
33734                                 (window.URL && URL.revokeObjectURL && URL) || 
33735                                 (window.webkitURL && webkitURL);
33736                         
33737         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
33738         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33739         
33740         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
33741         this.selectorEl.hide();
33742         
33743         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
33744         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33745         
33746         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
33747         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33748         this.thumbEl.hide();
33749         
33750         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
33751         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33752         
33753         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
33754         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33755         this.errorEl.hide();
33756         
33757         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
33758         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33759         this.footerEl.hide();
33760         
33761         this.setThumbBoxSize();
33762         
33763         this.bind();
33764         
33765         this.resize();
33766         
33767         this.fireEvent('initial', this);
33768     },
33769
33770     bind : function()
33771     {
33772         var _this = this;
33773         
33774         window.addEventListener("resize", function() { _this.resize(); } );
33775         
33776         this.bodyEl.on('click', this.beforeSelectFile, this);
33777         
33778         if(Roo.isTouch){
33779             this.bodyEl.on('touchstart', this.onTouchStart, this);
33780             this.bodyEl.on('touchmove', this.onTouchMove, this);
33781             this.bodyEl.on('touchend', this.onTouchEnd, this);
33782         }
33783         
33784         if(!Roo.isTouch){
33785             this.bodyEl.on('mousedown', this.onMouseDown, this);
33786             this.bodyEl.on('mousemove', this.onMouseMove, this);
33787             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
33788             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
33789             Roo.get(document).on('mouseup', this.onMouseUp, this);
33790         }
33791         
33792         this.selectorEl.on('change', this.onFileSelected, this);
33793     },
33794     
33795     reset : function()
33796     {    
33797         this.scale = 0;
33798         this.baseScale = 1;
33799         this.rotate = 0;
33800         this.baseRotate = 1;
33801         this.dragable = false;
33802         this.pinching = false;
33803         this.mouseX = 0;
33804         this.mouseY = 0;
33805         this.cropData = false;
33806         this.notifyEl.dom.innerHTML = this.emptyText;
33807         
33808         this.selectorEl.dom.value = '';
33809         
33810     },
33811     
33812     resize : function()
33813     {
33814         if(this.fireEvent('resize', this) != false){
33815             this.setThumbBoxPosition();
33816             this.setCanvasPosition();
33817         }
33818     },
33819     
33820     onFooterButtonClick : function(e, el, o, type)
33821     {
33822         switch (type) {
33823             case 'rotate-left' :
33824                 this.onRotateLeft(e);
33825                 break;
33826             case 'rotate-right' :
33827                 this.onRotateRight(e);
33828                 break;
33829             case 'picture' :
33830                 this.beforeSelectFile(e);
33831                 break;
33832             case 'trash' :
33833                 this.trash(e);
33834                 break;
33835             case 'crop' :
33836                 this.crop(e);
33837                 break;
33838             case 'download' :
33839                 this.download(e);
33840                 break;
33841             default :
33842                 break;
33843         }
33844         
33845         this.fireEvent('footerbuttonclick', this, type);
33846     },
33847     
33848     beforeSelectFile : function(e)
33849     {
33850         e.preventDefault();
33851         
33852         if(this.fireEvent('beforeselectfile', this) != false){
33853             this.selectorEl.dom.click();
33854         }
33855     },
33856     
33857     onFileSelected : function(e)
33858     {
33859         e.preventDefault();
33860         
33861         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
33862             return;
33863         }
33864         
33865         var file = this.selectorEl.dom.files[0];
33866         
33867         if(this.fireEvent('inspect', this, file) != false){
33868             this.prepare(file);
33869         }
33870         
33871     },
33872     
33873     trash : function(e)
33874     {
33875         this.fireEvent('trash', this);
33876     },
33877     
33878     download : function(e)
33879     {
33880         this.fireEvent('download', this);
33881     },
33882     
33883     loadCanvas : function(src)
33884     {   
33885         if(this.fireEvent('beforeloadcanvas', this, src) != false){
33886             
33887             this.reset();
33888             
33889             this.imageEl = document.createElement('img');
33890             
33891             var _this = this;
33892             
33893             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
33894             
33895             this.imageEl.src = src;
33896         }
33897     },
33898     
33899     onLoadCanvas : function()
33900     {   
33901         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
33902         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
33903         
33904         this.bodyEl.un('click', this.beforeSelectFile, this);
33905         
33906         this.notifyEl.hide();
33907         this.thumbEl.show();
33908         this.footerEl.show();
33909         
33910         this.baseRotateLevel();
33911         
33912         if(this.isDocument){
33913             this.setThumbBoxSize();
33914         }
33915         
33916         this.setThumbBoxPosition();
33917         
33918         this.baseScaleLevel();
33919         
33920         this.draw();
33921         
33922         this.resize();
33923         
33924         this.canvasLoaded = true;
33925         
33926         if(this.loadMask){
33927             this.maskEl.unmask();
33928         }
33929         
33930     },
33931     
33932     setCanvasPosition : function()
33933     {   
33934         if(!this.canvasEl){
33935             return;
33936         }
33937         
33938         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
33939         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
33940         
33941         this.previewEl.setLeft(pw);
33942         this.previewEl.setTop(ph);
33943         
33944     },
33945     
33946     onMouseDown : function(e)
33947     {   
33948         e.stopEvent();
33949         
33950         this.dragable = true;
33951         this.pinching = false;
33952         
33953         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
33954             this.dragable = false;
33955             return;
33956         }
33957         
33958         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33959         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33960         
33961     },
33962     
33963     onMouseMove : function(e)
33964     {   
33965         e.stopEvent();
33966         
33967         if(!this.canvasLoaded){
33968             return;
33969         }
33970         
33971         if (!this.dragable){
33972             return;
33973         }
33974         
33975         var minX = Math.ceil(this.thumbEl.getLeft(true));
33976         var minY = Math.ceil(this.thumbEl.getTop(true));
33977         
33978         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
33979         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
33980         
33981         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33982         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33983         
33984         x = x - this.mouseX;
33985         y = y - this.mouseY;
33986         
33987         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
33988         var bgY = Math.ceil(y + this.previewEl.getTop(true));
33989         
33990         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
33991         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
33992         
33993         this.previewEl.setLeft(bgX);
33994         this.previewEl.setTop(bgY);
33995         
33996         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33997         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33998     },
33999     
34000     onMouseUp : function(e)
34001     {   
34002         e.stopEvent();
34003         
34004         this.dragable = false;
34005     },
34006     
34007     onMouseWheel : function(e)
34008     {   
34009         e.stopEvent();
34010         
34011         this.startScale = this.scale;
34012         
34013         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
34014         
34015         if(!this.zoomable()){
34016             this.scale = this.startScale;
34017             return;
34018         }
34019         
34020         this.draw();
34021         
34022         return;
34023     },
34024     
34025     zoomable : function()
34026     {
34027         var minScale = this.thumbEl.getWidth() / this.minWidth;
34028         
34029         if(this.minWidth < this.minHeight){
34030             minScale = this.thumbEl.getHeight() / this.minHeight;
34031         }
34032         
34033         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
34034         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
34035         
34036         if(
34037                 this.isDocument &&
34038                 (this.rotate == 0 || this.rotate == 180) && 
34039                 (
34040                     width > this.imageEl.OriginWidth || 
34041                     height > this.imageEl.OriginHeight ||
34042                     (width < this.minWidth && height < this.minHeight)
34043                 )
34044         ){
34045             return false;
34046         }
34047         
34048         if(
34049                 this.isDocument &&
34050                 (this.rotate == 90 || this.rotate == 270) && 
34051                 (
34052                     width > this.imageEl.OriginWidth || 
34053                     height > this.imageEl.OriginHeight ||
34054                     (width < this.minHeight && height < this.minWidth)
34055                 )
34056         ){
34057             return false;
34058         }
34059         
34060         if(
34061                 !this.isDocument &&
34062                 (this.rotate == 0 || this.rotate == 180) && 
34063                 (
34064                     width < this.minWidth || 
34065                     width > this.imageEl.OriginWidth || 
34066                     height < this.minHeight || 
34067                     height > this.imageEl.OriginHeight
34068                 )
34069         ){
34070             return false;
34071         }
34072         
34073         if(
34074                 !this.isDocument &&
34075                 (this.rotate == 90 || this.rotate == 270) && 
34076                 (
34077                     width < this.minHeight || 
34078                     width > this.imageEl.OriginWidth || 
34079                     height < this.minWidth || 
34080                     height > this.imageEl.OriginHeight
34081                 )
34082         ){
34083             return false;
34084         }
34085         
34086         return true;
34087         
34088     },
34089     
34090     onRotateLeft : function(e)
34091     {   
34092         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
34093             
34094             var minScale = this.thumbEl.getWidth() / this.minWidth;
34095             
34096             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
34097             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
34098             
34099             this.startScale = this.scale;
34100             
34101             while (this.getScaleLevel() < minScale){
34102             
34103                 this.scale = this.scale + 1;
34104                 
34105                 if(!this.zoomable()){
34106                     break;
34107                 }
34108                 
34109                 if(
34110                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
34111                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
34112                 ){
34113                     continue;
34114                 }
34115                 
34116                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
34117
34118                 this.draw();
34119                 
34120                 return;
34121             }
34122             
34123             this.scale = this.startScale;
34124             
34125             this.onRotateFail();
34126             
34127             return false;
34128         }
34129         
34130         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
34131
34132         if(this.isDocument){
34133             this.setThumbBoxSize();
34134             this.setThumbBoxPosition();
34135             this.setCanvasPosition();
34136         }
34137         
34138         this.draw();
34139         
34140         this.fireEvent('rotate', this, 'left');
34141         
34142     },
34143     
34144     onRotateRight : function(e)
34145     {
34146         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
34147             
34148             var minScale = this.thumbEl.getWidth() / this.minWidth;
34149         
34150             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
34151             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
34152             
34153             this.startScale = this.scale;
34154             
34155             while (this.getScaleLevel() < minScale){
34156             
34157                 this.scale = this.scale + 1;
34158                 
34159                 if(!this.zoomable()){
34160                     break;
34161                 }
34162                 
34163                 if(
34164                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
34165                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
34166                 ){
34167                     continue;
34168                 }
34169                 
34170                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
34171
34172                 this.draw();
34173                 
34174                 return;
34175             }
34176             
34177             this.scale = this.startScale;
34178             
34179             this.onRotateFail();
34180             
34181             return false;
34182         }
34183         
34184         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
34185
34186         if(this.isDocument){
34187             this.setThumbBoxSize();
34188             this.setThumbBoxPosition();
34189             this.setCanvasPosition();
34190         }
34191         
34192         this.draw();
34193         
34194         this.fireEvent('rotate', this, 'right');
34195     },
34196     
34197     onRotateFail : function()
34198     {
34199         this.errorEl.show(true);
34200         
34201         var _this = this;
34202         
34203         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
34204     },
34205     
34206     draw : function()
34207     {
34208         this.previewEl.dom.innerHTML = '';
34209         
34210         var canvasEl = document.createElement("canvas");
34211         
34212         var contextEl = canvasEl.getContext("2d");
34213         
34214         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34215         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34216         var center = this.imageEl.OriginWidth / 2;
34217         
34218         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
34219             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34220             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34221             center = this.imageEl.OriginHeight / 2;
34222         }
34223         
34224         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
34225         
34226         contextEl.translate(center, center);
34227         contextEl.rotate(this.rotate * Math.PI / 180);
34228
34229         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34230         
34231         this.canvasEl = document.createElement("canvas");
34232         
34233         this.contextEl = this.canvasEl.getContext("2d");
34234         
34235         switch (this.rotate) {
34236             case 0 :
34237                 
34238                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34239                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34240                 
34241                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34242                 
34243                 break;
34244             case 90 : 
34245                 
34246                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34247                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34248                 
34249                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34250                     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);
34251                     break;
34252                 }
34253                 
34254                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34255                 
34256                 break;
34257             case 180 :
34258                 
34259                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34260                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34261                 
34262                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34263                     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);
34264                     break;
34265                 }
34266                 
34267                 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);
34268                 
34269                 break;
34270             case 270 :
34271                 
34272                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34273                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34274         
34275                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34276                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34277                     break;
34278                 }
34279                 
34280                 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);
34281                 
34282                 break;
34283             default : 
34284                 break;
34285         }
34286         
34287         this.previewEl.appendChild(this.canvasEl);
34288         
34289         this.setCanvasPosition();
34290     },
34291     
34292     crop : function()
34293     {
34294         if(!this.canvasLoaded){
34295             return;
34296         }
34297         
34298         var imageCanvas = document.createElement("canvas");
34299         
34300         var imageContext = imageCanvas.getContext("2d");
34301         
34302         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34303         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34304         
34305         var center = imageCanvas.width / 2;
34306         
34307         imageContext.translate(center, center);
34308         
34309         imageContext.rotate(this.rotate * Math.PI / 180);
34310         
34311         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34312         
34313         var canvas = document.createElement("canvas");
34314         
34315         var context = canvas.getContext("2d");
34316                 
34317         canvas.width = this.minWidth;
34318         canvas.height = this.minHeight;
34319
34320         switch (this.rotate) {
34321             case 0 :
34322                 
34323                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34324                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34325                 
34326                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34327                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34328                 
34329                 var targetWidth = this.minWidth - 2 * x;
34330                 var targetHeight = this.minHeight - 2 * y;
34331                 
34332                 var scale = 1;
34333                 
34334                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34335                     scale = targetWidth / width;
34336                 }
34337                 
34338                 if(x > 0 && y == 0){
34339                     scale = targetHeight / height;
34340                 }
34341                 
34342                 if(x > 0 && y > 0){
34343                     scale = targetWidth / width;
34344                     
34345                     if(width < height){
34346                         scale = targetHeight / height;
34347                     }
34348                 }
34349                 
34350                 context.scale(scale, scale);
34351                 
34352                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34353                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34354
34355                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34356                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34357
34358                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34359                 
34360                 break;
34361             case 90 : 
34362                 
34363                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34364                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34365                 
34366                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34367                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34368                 
34369                 var targetWidth = this.minWidth - 2 * x;
34370                 var targetHeight = this.minHeight - 2 * y;
34371                 
34372                 var scale = 1;
34373                 
34374                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34375                     scale = targetWidth / width;
34376                 }
34377                 
34378                 if(x > 0 && y == 0){
34379                     scale = targetHeight / height;
34380                 }
34381                 
34382                 if(x > 0 && y > 0){
34383                     scale = targetWidth / width;
34384                     
34385                     if(width < height){
34386                         scale = targetHeight / height;
34387                     }
34388                 }
34389                 
34390                 context.scale(scale, scale);
34391                 
34392                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34393                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34394
34395                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34396                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34397                 
34398                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34399                 
34400                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34401                 
34402                 break;
34403             case 180 :
34404                 
34405                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34406                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34407                 
34408                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34409                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34410                 
34411                 var targetWidth = this.minWidth - 2 * x;
34412                 var targetHeight = this.minHeight - 2 * y;
34413                 
34414                 var scale = 1;
34415                 
34416                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34417                     scale = targetWidth / width;
34418                 }
34419                 
34420                 if(x > 0 && y == 0){
34421                     scale = targetHeight / height;
34422                 }
34423                 
34424                 if(x > 0 && y > 0){
34425                     scale = targetWidth / width;
34426                     
34427                     if(width < height){
34428                         scale = targetHeight / height;
34429                     }
34430                 }
34431                 
34432                 context.scale(scale, scale);
34433                 
34434                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34435                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34436
34437                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34438                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34439
34440                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34441                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34442                 
34443                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34444                 
34445                 break;
34446             case 270 :
34447                 
34448                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34449                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34450                 
34451                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34452                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34453                 
34454                 var targetWidth = this.minWidth - 2 * x;
34455                 var targetHeight = this.minHeight - 2 * y;
34456                 
34457                 var scale = 1;
34458                 
34459                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34460                     scale = targetWidth / width;
34461                 }
34462                 
34463                 if(x > 0 && y == 0){
34464                     scale = targetHeight / height;
34465                 }
34466                 
34467                 if(x > 0 && y > 0){
34468                     scale = targetWidth / width;
34469                     
34470                     if(width < height){
34471                         scale = targetHeight / height;
34472                     }
34473                 }
34474                 
34475                 context.scale(scale, scale);
34476                 
34477                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34478                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34479
34480                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34481                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34482                 
34483                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34484                 
34485                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34486                 
34487                 break;
34488             default : 
34489                 break;
34490         }
34491         
34492         this.cropData = canvas.toDataURL(this.cropType);
34493         
34494         if(this.fireEvent('crop', this, this.cropData) !== false){
34495             this.process(this.file, this.cropData);
34496         }
34497         
34498         return;
34499         
34500     },
34501     
34502     setThumbBoxSize : function()
34503     {
34504         var width, height;
34505         
34506         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
34507             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
34508             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
34509             
34510             this.minWidth = width;
34511             this.minHeight = height;
34512             
34513             if(this.rotate == 90 || this.rotate == 270){
34514                 this.minWidth = height;
34515                 this.minHeight = width;
34516             }
34517         }
34518         
34519         height = 300;
34520         width = Math.ceil(this.minWidth * height / this.minHeight);
34521         
34522         if(this.minWidth > this.minHeight){
34523             width = 300;
34524             height = Math.ceil(this.minHeight * width / this.minWidth);
34525         }
34526         
34527         this.thumbEl.setStyle({
34528             width : width + 'px',
34529             height : height + 'px'
34530         });
34531
34532         return;
34533             
34534     },
34535     
34536     setThumbBoxPosition : function()
34537     {
34538         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
34539         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
34540         
34541         this.thumbEl.setLeft(x);
34542         this.thumbEl.setTop(y);
34543         
34544     },
34545     
34546     baseRotateLevel : function()
34547     {
34548         this.baseRotate = 1;
34549         
34550         if(
34551                 typeof(this.exif) != 'undefined' &&
34552                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
34553                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
34554         ){
34555             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
34556         }
34557         
34558         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
34559         
34560     },
34561     
34562     baseScaleLevel : function()
34563     {
34564         var width, height;
34565         
34566         if(this.isDocument){
34567             
34568             if(this.baseRotate == 6 || this.baseRotate == 8){
34569             
34570                 height = this.thumbEl.getHeight();
34571                 this.baseScale = height / this.imageEl.OriginWidth;
34572
34573                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
34574                     width = this.thumbEl.getWidth();
34575                     this.baseScale = width / this.imageEl.OriginHeight;
34576                 }
34577
34578                 return;
34579             }
34580
34581             height = this.thumbEl.getHeight();
34582             this.baseScale = height / this.imageEl.OriginHeight;
34583
34584             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
34585                 width = this.thumbEl.getWidth();
34586                 this.baseScale = width / this.imageEl.OriginWidth;
34587             }
34588
34589             return;
34590         }
34591         
34592         if(this.baseRotate == 6 || this.baseRotate == 8){
34593             
34594             width = this.thumbEl.getHeight();
34595             this.baseScale = width / this.imageEl.OriginHeight;
34596             
34597             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
34598                 height = this.thumbEl.getWidth();
34599                 this.baseScale = height / this.imageEl.OriginHeight;
34600             }
34601             
34602             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34603                 height = this.thumbEl.getWidth();
34604                 this.baseScale = height / this.imageEl.OriginHeight;
34605                 
34606                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
34607                     width = this.thumbEl.getHeight();
34608                     this.baseScale = width / this.imageEl.OriginWidth;
34609                 }
34610             }
34611             
34612             return;
34613         }
34614         
34615         width = this.thumbEl.getWidth();
34616         this.baseScale = width / this.imageEl.OriginWidth;
34617         
34618         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
34619             height = this.thumbEl.getHeight();
34620             this.baseScale = height / this.imageEl.OriginHeight;
34621         }
34622         
34623         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34624             
34625             height = this.thumbEl.getHeight();
34626             this.baseScale = height / this.imageEl.OriginHeight;
34627             
34628             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
34629                 width = this.thumbEl.getWidth();
34630                 this.baseScale = width / this.imageEl.OriginWidth;
34631             }
34632             
34633         }
34634         
34635         return;
34636     },
34637     
34638     getScaleLevel : function()
34639     {
34640         return this.baseScale * Math.pow(1.1, this.scale);
34641     },
34642     
34643     onTouchStart : function(e)
34644     {
34645         if(!this.canvasLoaded){
34646             this.beforeSelectFile(e);
34647             return;
34648         }
34649         
34650         var touches = e.browserEvent.touches;
34651         
34652         if(!touches){
34653             return;
34654         }
34655         
34656         if(touches.length == 1){
34657             this.onMouseDown(e);
34658             return;
34659         }
34660         
34661         if(touches.length != 2){
34662             return;
34663         }
34664         
34665         var coords = [];
34666         
34667         for(var i = 0, finger; finger = touches[i]; i++){
34668             coords.push(finger.pageX, finger.pageY);
34669         }
34670         
34671         var x = Math.pow(coords[0] - coords[2], 2);
34672         var y = Math.pow(coords[1] - coords[3], 2);
34673         
34674         this.startDistance = Math.sqrt(x + y);
34675         
34676         this.startScale = this.scale;
34677         
34678         this.pinching = true;
34679         this.dragable = false;
34680         
34681     },
34682     
34683     onTouchMove : function(e)
34684     {
34685         if(!this.pinching && !this.dragable){
34686             return;
34687         }
34688         
34689         var touches = e.browserEvent.touches;
34690         
34691         if(!touches){
34692             return;
34693         }
34694         
34695         if(this.dragable){
34696             this.onMouseMove(e);
34697             return;
34698         }
34699         
34700         var coords = [];
34701         
34702         for(var i = 0, finger; finger = touches[i]; i++){
34703             coords.push(finger.pageX, finger.pageY);
34704         }
34705         
34706         var x = Math.pow(coords[0] - coords[2], 2);
34707         var y = Math.pow(coords[1] - coords[3], 2);
34708         
34709         this.endDistance = Math.sqrt(x + y);
34710         
34711         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
34712         
34713         if(!this.zoomable()){
34714             this.scale = this.startScale;
34715             return;
34716         }
34717         
34718         this.draw();
34719         
34720     },
34721     
34722     onTouchEnd : function(e)
34723     {
34724         this.pinching = false;
34725         this.dragable = false;
34726         
34727     },
34728     
34729     process : function(file, crop)
34730     {
34731         if(this.loadMask){
34732             this.maskEl.mask(this.loadingText);
34733         }
34734         
34735         this.xhr = new XMLHttpRequest();
34736         
34737         file.xhr = this.xhr;
34738
34739         this.xhr.open(this.method, this.url, true);
34740         
34741         var headers = {
34742             "Accept": "application/json",
34743             "Cache-Control": "no-cache",
34744             "X-Requested-With": "XMLHttpRequest"
34745         };
34746         
34747         for (var headerName in headers) {
34748             var headerValue = headers[headerName];
34749             if (headerValue) {
34750                 this.xhr.setRequestHeader(headerName, headerValue);
34751             }
34752         }
34753         
34754         var _this = this;
34755         
34756         this.xhr.onload = function()
34757         {
34758             _this.xhrOnLoad(_this.xhr);
34759         }
34760         
34761         this.xhr.onerror = function()
34762         {
34763             _this.xhrOnError(_this.xhr);
34764         }
34765         
34766         var formData = new FormData();
34767
34768         formData.append('returnHTML', 'NO');
34769         
34770         if(crop){
34771             formData.append('crop', crop);
34772         }
34773         
34774         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
34775             formData.append(this.paramName, file, file.name);
34776         }
34777         
34778         if(typeof(file.filename) != 'undefined'){
34779             formData.append('filename', file.filename);
34780         }
34781         
34782         if(typeof(file.mimetype) != 'undefined'){
34783             formData.append('mimetype', file.mimetype);
34784         }
34785         
34786         if(this.fireEvent('arrange', this, formData) != false){
34787             this.xhr.send(formData);
34788         };
34789     },
34790     
34791     xhrOnLoad : function(xhr)
34792     {
34793         if(this.loadMask){
34794             this.maskEl.unmask();
34795         }
34796         
34797         if (xhr.readyState !== 4) {
34798             this.fireEvent('exception', this, xhr);
34799             return;
34800         }
34801
34802         var response = Roo.decode(xhr.responseText);
34803         
34804         if(!response.success){
34805             this.fireEvent('exception', this, xhr);
34806             return;
34807         }
34808         
34809         var response = Roo.decode(xhr.responseText);
34810         
34811         this.fireEvent('upload', this, response);
34812         
34813     },
34814     
34815     xhrOnError : function()
34816     {
34817         if(this.loadMask){
34818             this.maskEl.unmask();
34819         }
34820         
34821         Roo.log('xhr on error');
34822         
34823         var response = Roo.decode(xhr.responseText);
34824           
34825         Roo.log(response);
34826         
34827     },
34828     
34829     prepare : function(file)
34830     {   
34831         if(this.loadMask){
34832             this.maskEl.mask(this.loadingText);
34833         }
34834         
34835         this.file = false;
34836         this.exif = {};
34837         
34838         if(typeof(file) === 'string'){
34839             this.loadCanvas(file);
34840             return;
34841         }
34842         
34843         if(!file || !this.urlAPI){
34844             return;
34845         }
34846         
34847         this.file = file;
34848         this.cropType = file.type;
34849         
34850         var _this = this;
34851         
34852         if(this.fireEvent('prepare', this, this.file) != false){
34853             
34854             var reader = new FileReader();
34855             
34856             reader.onload = function (e) {
34857                 if (e.target.error) {
34858                     Roo.log(e.target.error);
34859                     return;
34860                 }
34861                 
34862                 var buffer = e.target.result,
34863                     dataView = new DataView(buffer),
34864                     offset = 2,
34865                     maxOffset = dataView.byteLength - 4,
34866                     markerBytes,
34867                     markerLength;
34868                 
34869                 if (dataView.getUint16(0) === 0xffd8) {
34870                     while (offset < maxOffset) {
34871                         markerBytes = dataView.getUint16(offset);
34872                         
34873                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
34874                             markerLength = dataView.getUint16(offset + 2) + 2;
34875                             if (offset + markerLength > dataView.byteLength) {
34876                                 Roo.log('Invalid meta data: Invalid segment size.');
34877                                 break;
34878                             }
34879                             
34880                             if(markerBytes == 0xffe1){
34881                                 _this.parseExifData(
34882                                     dataView,
34883                                     offset,
34884                                     markerLength
34885                                 );
34886                             }
34887                             
34888                             offset += markerLength;
34889                             
34890                             continue;
34891                         }
34892                         
34893                         break;
34894                     }
34895                     
34896                 }
34897                 
34898                 var url = _this.urlAPI.createObjectURL(_this.file);
34899                 
34900                 _this.loadCanvas(url);
34901                 
34902                 return;
34903             }
34904             
34905             reader.readAsArrayBuffer(this.file);
34906             
34907         }
34908         
34909     },
34910     
34911     parseExifData : function(dataView, offset, length)
34912     {
34913         var tiffOffset = offset + 10,
34914             littleEndian,
34915             dirOffset;
34916     
34917         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34918             // No Exif data, might be XMP data instead
34919             return;
34920         }
34921         
34922         // Check for the ASCII code for "Exif" (0x45786966):
34923         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34924             // No Exif data, might be XMP data instead
34925             return;
34926         }
34927         if (tiffOffset + 8 > dataView.byteLength) {
34928             Roo.log('Invalid Exif data: Invalid segment size.');
34929             return;
34930         }
34931         // Check for the two null bytes:
34932         if (dataView.getUint16(offset + 8) !== 0x0000) {
34933             Roo.log('Invalid Exif data: Missing byte alignment offset.');
34934             return;
34935         }
34936         // Check the byte alignment:
34937         switch (dataView.getUint16(tiffOffset)) {
34938         case 0x4949:
34939             littleEndian = true;
34940             break;
34941         case 0x4D4D:
34942             littleEndian = false;
34943             break;
34944         default:
34945             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
34946             return;
34947         }
34948         // Check for the TIFF tag marker (0x002A):
34949         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
34950             Roo.log('Invalid Exif data: Missing TIFF marker.');
34951             return;
34952         }
34953         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
34954         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
34955         
34956         this.parseExifTags(
34957             dataView,
34958             tiffOffset,
34959             tiffOffset + dirOffset,
34960             littleEndian
34961         );
34962     },
34963     
34964     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
34965     {
34966         var tagsNumber,
34967             dirEndOffset,
34968             i;
34969         if (dirOffset + 6 > dataView.byteLength) {
34970             Roo.log('Invalid Exif data: Invalid directory offset.');
34971             return;
34972         }
34973         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
34974         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
34975         if (dirEndOffset + 4 > dataView.byteLength) {
34976             Roo.log('Invalid Exif data: Invalid directory size.');
34977             return;
34978         }
34979         for (i = 0; i < tagsNumber; i += 1) {
34980             this.parseExifTag(
34981                 dataView,
34982                 tiffOffset,
34983                 dirOffset + 2 + 12 * i, // tag offset
34984                 littleEndian
34985             );
34986         }
34987         // Return the offset to the next directory:
34988         return dataView.getUint32(dirEndOffset, littleEndian);
34989     },
34990     
34991     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
34992     {
34993         var tag = dataView.getUint16(offset, littleEndian);
34994         
34995         this.exif[tag] = this.getExifValue(
34996             dataView,
34997             tiffOffset,
34998             offset,
34999             dataView.getUint16(offset + 2, littleEndian), // tag type
35000             dataView.getUint32(offset + 4, littleEndian), // tag length
35001             littleEndian
35002         );
35003     },
35004     
35005     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
35006     {
35007         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
35008             tagSize,
35009             dataOffset,
35010             values,
35011             i,
35012             str,
35013             c;
35014     
35015         if (!tagType) {
35016             Roo.log('Invalid Exif data: Invalid tag type.');
35017             return;
35018         }
35019         
35020         tagSize = tagType.size * length;
35021         // Determine if the value is contained in the dataOffset bytes,
35022         // or if the value at the dataOffset is a pointer to the actual data:
35023         dataOffset = tagSize > 4 ?
35024                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
35025         if (dataOffset + tagSize > dataView.byteLength) {
35026             Roo.log('Invalid Exif data: Invalid data offset.');
35027             return;
35028         }
35029         if (length === 1) {
35030             return tagType.getValue(dataView, dataOffset, littleEndian);
35031         }
35032         values = [];
35033         for (i = 0; i < length; i += 1) {
35034             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
35035         }
35036         
35037         if (tagType.ascii) {
35038             str = '';
35039             // Concatenate the chars:
35040             for (i = 0; i < values.length; i += 1) {
35041                 c = values[i];
35042                 // Ignore the terminating NULL byte(s):
35043                 if (c === '\u0000') {
35044                     break;
35045                 }
35046                 str += c;
35047             }
35048             return str;
35049         }
35050         return values;
35051     }
35052     
35053 });
35054
35055 Roo.apply(Roo.bootstrap.UploadCropbox, {
35056     tags : {
35057         'Orientation': 0x0112
35058     },
35059     
35060     Orientation: {
35061             1: 0, //'top-left',
35062 //            2: 'top-right',
35063             3: 180, //'bottom-right',
35064 //            4: 'bottom-left',
35065 //            5: 'left-top',
35066             6: 90, //'right-top',
35067 //            7: 'right-bottom',
35068             8: 270 //'left-bottom'
35069     },
35070     
35071     exifTagTypes : {
35072         // byte, 8-bit unsigned int:
35073         1: {
35074             getValue: function (dataView, dataOffset) {
35075                 return dataView.getUint8(dataOffset);
35076             },
35077             size: 1
35078         },
35079         // ascii, 8-bit byte:
35080         2: {
35081             getValue: function (dataView, dataOffset) {
35082                 return String.fromCharCode(dataView.getUint8(dataOffset));
35083             },
35084             size: 1,
35085             ascii: true
35086         },
35087         // short, 16 bit int:
35088         3: {
35089             getValue: function (dataView, dataOffset, littleEndian) {
35090                 return dataView.getUint16(dataOffset, littleEndian);
35091             },
35092             size: 2
35093         },
35094         // long, 32 bit int:
35095         4: {
35096             getValue: function (dataView, dataOffset, littleEndian) {
35097                 return dataView.getUint32(dataOffset, littleEndian);
35098             },
35099             size: 4
35100         },
35101         // rational = two long values, first is numerator, second is denominator:
35102         5: {
35103             getValue: function (dataView, dataOffset, littleEndian) {
35104                 return dataView.getUint32(dataOffset, littleEndian) /
35105                     dataView.getUint32(dataOffset + 4, littleEndian);
35106             },
35107             size: 8
35108         },
35109         // slong, 32 bit signed int:
35110         9: {
35111             getValue: function (dataView, dataOffset, littleEndian) {
35112                 return dataView.getInt32(dataOffset, littleEndian);
35113             },
35114             size: 4
35115         },
35116         // srational, two slongs, first is numerator, second is denominator:
35117         10: {
35118             getValue: function (dataView, dataOffset, littleEndian) {
35119                 return dataView.getInt32(dataOffset, littleEndian) /
35120                     dataView.getInt32(dataOffset + 4, littleEndian);
35121             },
35122             size: 8
35123         }
35124     },
35125     
35126     footer : {
35127         STANDARD : [
35128             {
35129                 tag : 'div',
35130                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35131                 action : 'rotate-left',
35132                 cn : [
35133                     {
35134                         tag : 'button',
35135                         cls : 'btn btn-default',
35136                         html : '<i class="fa fa-undo"></i>'
35137                     }
35138                 ]
35139             },
35140             {
35141                 tag : 'div',
35142                 cls : 'btn-group roo-upload-cropbox-picture',
35143                 action : 'picture',
35144                 cn : [
35145                     {
35146                         tag : 'button',
35147                         cls : 'btn btn-default',
35148                         html : '<i class="fa fa-picture-o"></i>'
35149                     }
35150                 ]
35151             },
35152             {
35153                 tag : 'div',
35154                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35155                 action : 'rotate-right',
35156                 cn : [
35157                     {
35158                         tag : 'button',
35159                         cls : 'btn btn-default',
35160                         html : '<i class="fa fa-repeat"></i>'
35161                     }
35162                 ]
35163             }
35164         ],
35165         DOCUMENT : [
35166             {
35167                 tag : 'div',
35168                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35169                 action : 'rotate-left',
35170                 cn : [
35171                     {
35172                         tag : 'button',
35173                         cls : 'btn btn-default',
35174                         html : '<i class="fa fa-undo"></i>'
35175                     }
35176                 ]
35177             },
35178             {
35179                 tag : 'div',
35180                 cls : 'btn-group roo-upload-cropbox-download',
35181                 action : 'download',
35182                 cn : [
35183                     {
35184                         tag : 'button',
35185                         cls : 'btn btn-default',
35186                         html : '<i class="fa fa-download"></i>'
35187                     }
35188                 ]
35189             },
35190             {
35191                 tag : 'div',
35192                 cls : 'btn-group roo-upload-cropbox-crop',
35193                 action : 'crop',
35194                 cn : [
35195                     {
35196                         tag : 'button',
35197                         cls : 'btn btn-default',
35198                         html : '<i class="fa fa-crop"></i>'
35199                     }
35200                 ]
35201             },
35202             {
35203                 tag : 'div',
35204                 cls : 'btn-group roo-upload-cropbox-trash',
35205                 action : 'trash',
35206                 cn : [
35207                     {
35208                         tag : 'button',
35209                         cls : 'btn btn-default',
35210                         html : '<i class="fa fa-trash"></i>'
35211                     }
35212                 ]
35213             },
35214             {
35215                 tag : 'div',
35216                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35217                 action : 'rotate-right',
35218                 cn : [
35219                     {
35220                         tag : 'button',
35221                         cls : 'btn btn-default',
35222                         html : '<i class="fa fa-repeat"></i>'
35223                     }
35224                 ]
35225             }
35226         ],
35227         ROTATOR : [
35228             {
35229                 tag : 'div',
35230                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35231                 action : 'rotate-left',
35232                 cn : [
35233                     {
35234                         tag : 'button',
35235                         cls : 'btn btn-default',
35236                         html : '<i class="fa fa-undo"></i>'
35237                     }
35238                 ]
35239             },
35240             {
35241                 tag : 'div',
35242                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35243                 action : 'rotate-right',
35244                 cn : [
35245                     {
35246                         tag : 'button',
35247                         cls : 'btn btn-default',
35248                         html : '<i class="fa fa-repeat"></i>'
35249                     }
35250                 ]
35251             }
35252         ]
35253     }
35254 });
35255
35256 /*
35257 * Licence: LGPL
35258 */
35259
35260 /**
35261  * @class Roo.bootstrap.DocumentManager
35262  * @extends Roo.bootstrap.Component
35263  * Bootstrap DocumentManager class
35264  * @cfg {String} paramName default 'imageUpload'
35265  * @cfg {String} toolTipName default 'filename'
35266  * @cfg {String} method default POST
35267  * @cfg {String} url action url
35268  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
35269  * @cfg {Boolean} multiple multiple upload default true
35270  * @cfg {Number} thumbSize default 300
35271  * @cfg {String} fieldLabel
35272  * @cfg {Number} labelWidth default 4
35273  * @cfg {String} labelAlign (left|top) default left
35274  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
35275 * @cfg {Number} labellg set the width of label (1-12)
35276  * @cfg {Number} labelmd set the width of label (1-12)
35277  * @cfg {Number} labelsm set the width of label (1-12)
35278  * @cfg {Number} labelxs set the width of label (1-12)
35279  * 
35280  * @constructor
35281  * Create a new DocumentManager
35282  * @param {Object} config The config object
35283  */
35284
35285 Roo.bootstrap.DocumentManager = function(config){
35286     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
35287     
35288     this.files = [];
35289     this.delegates = [];
35290     
35291     this.addEvents({
35292         /**
35293          * @event initial
35294          * Fire when initial the DocumentManager
35295          * @param {Roo.bootstrap.DocumentManager} this
35296          */
35297         "initial" : true,
35298         /**
35299          * @event inspect
35300          * inspect selected file
35301          * @param {Roo.bootstrap.DocumentManager} this
35302          * @param {File} file
35303          */
35304         "inspect" : true,
35305         /**
35306          * @event exception
35307          * Fire when xhr load exception
35308          * @param {Roo.bootstrap.DocumentManager} this
35309          * @param {XMLHttpRequest} xhr
35310          */
35311         "exception" : true,
35312         /**
35313          * @event afterupload
35314          * Fire when xhr load exception
35315          * @param {Roo.bootstrap.DocumentManager} this
35316          * @param {XMLHttpRequest} xhr
35317          */
35318         "afterupload" : true,
35319         /**
35320          * @event prepare
35321          * prepare the form data
35322          * @param {Roo.bootstrap.DocumentManager} this
35323          * @param {Object} formData
35324          */
35325         "prepare" : true,
35326         /**
35327          * @event remove
35328          * Fire when remove the file
35329          * @param {Roo.bootstrap.DocumentManager} this
35330          * @param {Object} file
35331          */
35332         "remove" : true,
35333         /**
35334          * @event refresh
35335          * Fire after refresh the file
35336          * @param {Roo.bootstrap.DocumentManager} this
35337          */
35338         "refresh" : true,
35339         /**
35340          * @event click
35341          * Fire after click the image
35342          * @param {Roo.bootstrap.DocumentManager} this
35343          * @param {Object} file
35344          */
35345         "click" : true,
35346         /**
35347          * @event edit
35348          * Fire when upload a image and editable set to true
35349          * @param {Roo.bootstrap.DocumentManager} this
35350          * @param {Object} file
35351          */
35352         "edit" : true,
35353         /**
35354          * @event beforeselectfile
35355          * Fire before select file
35356          * @param {Roo.bootstrap.DocumentManager} this
35357          */
35358         "beforeselectfile" : true,
35359         /**
35360          * @event process
35361          * Fire before process file
35362          * @param {Roo.bootstrap.DocumentManager} this
35363          * @param {Object} file
35364          */
35365         "process" : true,
35366         /**
35367          * @event previewrendered
35368          * Fire when preview rendered
35369          * @param {Roo.bootstrap.DocumentManager} this
35370          * @param {Object} file
35371          */
35372         "previewrendered" : true,
35373         /**
35374          */
35375         "previewResize" : true
35376         
35377     });
35378 };
35379
35380 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
35381     
35382     boxes : 0,
35383     inputName : '',
35384     thumbSize : 300,
35385     multiple : true,
35386     files : false,
35387     method : 'POST',
35388     url : '',
35389     paramName : 'imageUpload',
35390     toolTipName : 'filename',
35391     fieldLabel : '',
35392     labelWidth : 4,
35393     labelAlign : 'left',
35394     editable : true,
35395     delegates : false,
35396     xhr : false, 
35397     
35398     labellg : 0,
35399     labelmd : 0,
35400     labelsm : 0,
35401     labelxs : 0,
35402     
35403     getAutoCreate : function()
35404     {   
35405         var managerWidget = {
35406             tag : 'div',
35407             cls : 'roo-document-manager',
35408             cn : [
35409                 {
35410                     tag : 'input',
35411                     cls : 'roo-document-manager-selector',
35412                     type : 'file'
35413                 },
35414                 {
35415                     tag : 'div',
35416                     cls : 'roo-document-manager-uploader',
35417                     cn : [
35418                         {
35419                             tag : 'div',
35420                             cls : 'roo-document-manager-upload-btn',
35421                             html : '<i class="fa fa-plus"></i>'
35422                         }
35423                     ]
35424                     
35425                 }
35426             ]
35427         };
35428         
35429         var content = [
35430             {
35431                 tag : 'div',
35432                 cls : 'column col-md-12',
35433                 cn : managerWidget
35434             }
35435         ];
35436         
35437         if(this.fieldLabel.length){
35438             
35439             content = [
35440                 {
35441                     tag : 'div',
35442                     cls : 'column col-md-12',
35443                     html : this.fieldLabel
35444                 },
35445                 {
35446                     tag : 'div',
35447                     cls : 'column col-md-12',
35448                     cn : managerWidget
35449                 }
35450             ];
35451
35452             if(this.labelAlign == 'left'){
35453                 content = [
35454                     {
35455                         tag : 'div',
35456                         cls : 'column',
35457                         html : this.fieldLabel
35458                     },
35459                     {
35460                         tag : 'div',
35461                         cls : 'column',
35462                         cn : managerWidget
35463                     }
35464                 ];
35465                 
35466                 if(this.labelWidth > 12){
35467                     content[0].style = "width: " + this.labelWidth + 'px';
35468                 }
35469
35470                 if(this.labelWidth < 13 && this.labelmd == 0){
35471                     this.labelmd = this.labelWidth;
35472                 }
35473
35474                 if(this.labellg > 0){
35475                     content[0].cls += ' col-lg-' + this.labellg;
35476                     content[1].cls += ' col-lg-' + (12 - this.labellg);
35477                 }
35478
35479                 if(this.labelmd > 0){
35480                     content[0].cls += ' col-md-' + this.labelmd;
35481                     content[1].cls += ' col-md-' + (12 - this.labelmd);
35482                 }
35483
35484                 if(this.labelsm > 0){
35485                     content[0].cls += ' col-sm-' + this.labelsm;
35486                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
35487                 }
35488
35489                 if(this.labelxs > 0){
35490                     content[0].cls += ' col-xs-' + this.labelxs;
35491                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
35492                 }
35493                 
35494             }
35495         }
35496         
35497         var cfg = {
35498             tag : 'div',
35499             cls : 'row clearfix',
35500             cn : content
35501         };
35502         
35503         return cfg;
35504         
35505     },
35506     
35507     initEvents : function()
35508     {
35509         this.managerEl = this.el.select('.roo-document-manager', true).first();
35510         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35511         
35512         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
35513         this.selectorEl.hide();
35514         
35515         if(this.multiple){
35516             this.selectorEl.attr('multiple', 'multiple');
35517         }
35518         
35519         this.selectorEl.on('change', this.onFileSelected, this);
35520         
35521         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
35522         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35523         
35524         this.uploader.on('click', this.onUploaderClick, this);
35525         
35526         this.renderProgressDialog();
35527         
35528         var _this = this;
35529         
35530         window.addEventListener("resize", function() { _this.refresh(); } );
35531         
35532         this.fireEvent('initial', this);
35533     },
35534     
35535     renderProgressDialog : function()
35536     {
35537         var _this = this;
35538         
35539         this.progressDialog = new Roo.bootstrap.Modal({
35540             cls : 'roo-document-manager-progress-dialog',
35541             allow_close : false,
35542             animate : false,
35543             title : '',
35544             buttons : [
35545                 {
35546                     name  :'cancel',
35547                     weight : 'danger',
35548                     html : 'Cancel'
35549                 }
35550             ], 
35551             listeners : { 
35552                 btnclick : function() {
35553                     _this.uploadCancel();
35554                     this.hide();
35555                 }
35556             }
35557         });
35558          
35559         this.progressDialog.render(Roo.get(document.body));
35560          
35561         this.progress = new Roo.bootstrap.Progress({
35562             cls : 'roo-document-manager-progress',
35563             active : true,
35564             striped : true
35565         });
35566         
35567         this.progress.render(this.progressDialog.getChildContainer());
35568         
35569         this.progressBar = new Roo.bootstrap.ProgressBar({
35570             cls : 'roo-document-manager-progress-bar',
35571             aria_valuenow : 0,
35572             aria_valuemin : 0,
35573             aria_valuemax : 12,
35574             panel : 'success'
35575         });
35576         
35577         this.progressBar.render(this.progress.getChildContainer());
35578     },
35579     
35580     onUploaderClick : function(e)
35581     {
35582         e.preventDefault();
35583      
35584         if(this.fireEvent('beforeselectfile', this) != false){
35585             this.selectorEl.dom.click();
35586         }
35587         
35588     },
35589     
35590     onFileSelected : function(e)
35591     {
35592         e.preventDefault();
35593         
35594         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35595             return;
35596         }
35597         
35598         Roo.each(this.selectorEl.dom.files, function(file){
35599             if(this.fireEvent('inspect', this, file) != false){
35600                 this.files.push(file);
35601             }
35602         }, this);
35603         
35604         this.queue();
35605         
35606     },
35607     
35608     queue : function()
35609     {
35610         this.selectorEl.dom.value = '';
35611         
35612         if(!this.files || !this.files.length){
35613             return;
35614         }
35615         
35616         if(this.boxes > 0 && this.files.length > this.boxes){
35617             this.files = this.files.slice(0, this.boxes);
35618         }
35619         
35620         this.uploader.show();
35621         
35622         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35623             this.uploader.hide();
35624         }
35625         
35626         var _this = this;
35627         
35628         var files = [];
35629         
35630         var docs = [];
35631         
35632         Roo.each(this.files, function(file){
35633             
35634             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35635                 var f = this.renderPreview(file);
35636                 files.push(f);
35637                 return;
35638             }
35639             
35640             if(file.type.indexOf('image') != -1){
35641                 this.delegates.push(
35642                     (function(){
35643                         _this.process(file);
35644                     }).createDelegate(this)
35645                 );
35646         
35647                 return;
35648             }
35649             
35650             docs.push(
35651                 (function(){
35652                     _this.process(file);
35653                 }).createDelegate(this)
35654             );
35655             
35656         }, this);
35657         
35658         this.files = files;
35659         
35660         this.delegates = this.delegates.concat(docs);
35661         
35662         if(!this.delegates.length){
35663             this.refresh();
35664             return;
35665         }
35666         
35667         this.progressBar.aria_valuemax = this.delegates.length;
35668         
35669         this.arrange();
35670         
35671         return;
35672     },
35673     
35674     arrange : function()
35675     {
35676         if(!this.delegates.length){
35677             this.progressDialog.hide();
35678             this.refresh();
35679             return;
35680         }
35681         
35682         var delegate = this.delegates.shift();
35683         
35684         this.progressDialog.show();
35685         
35686         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
35687         
35688         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
35689         
35690         delegate();
35691     },
35692     
35693     refresh : function()
35694     {
35695         this.uploader.show();
35696         
35697         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35698             this.uploader.hide();
35699         }
35700         
35701         Roo.isTouch ? this.closable(false) : this.closable(true);
35702         
35703         this.fireEvent('refresh', this);
35704     },
35705     
35706     onRemove : function(e, el, o)
35707     {
35708         e.preventDefault();
35709         
35710         this.fireEvent('remove', this, o);
35711         
35712     },
35713     
35714     remove : function(o)
35715     {
35716         var files = [];
35717         
35718         Roo.each(this.files, function(file){
35719             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
35720                 files.push(file);
35721                 return;
35722             }
35723
35724             o.target.remove();
35725
35726         }, this);
35727         
35728         this.files = files;
35729         
35730         this.refresh();
35731     },
35732     
35733     clear : function()
35734     {
35735         Roo.each(this.files, function(file){
35736             if(!file.target){
35737                 return;
35738             }
35739             
35740             file.target.remove();
35741
35742         }, this);
35743         
35744         this.files = [];
35745         
35746         this.refresh();
35747     },
35748     
35749     onClick : function(e, el, o)
35750     {
35751         e.preventDefault();
35752         
35753         this.fireEvent('click', this, o);
35754         
35755     },
35756     
35757     closable : function(closable)
35758     {
35759         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
35760             
35761             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35762             
35763             if(closable){
35764                 el.show();
35765                 return;
35766             }
35767             
35768             el.hide();
35769             
35770         }, this);
35771     },
35772     
35773     xhrOnLoad : function(xhr)
35774     {
35775         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35776             el.remove();
35777         }, this);
35778         
35779         if (xhr.readyState !== 4) {
35780             this.arrange();
35781             this.fireEvent('exception', this, xhr);
35782             return;
35783         }
35784
35785         var response = Roo.decode(xhr.responseText);
35786         
35787         if(!response.success){
35788             this.arrange();
35789             this.fireEvent('exception', this, xhr);
35790             return;
35791         }
35792         
35793         var file = this.renderPreview(response.data);
35794         
35795         this.files.push(file);
35796         
35797         this.arrange();
35798         
35799         this.fireEvent('afterupload', this, xhr);
35800         
35801     },
35802     
35803     xhrOnError : function(xhr)
35804     {
35805         Roo.log('xhr on error');
35806         
35807         var response = Roo.decode(xhr.responseText);
35808           
35809         Roo.log(response);
35810         
35811         this.arrange();
35812     },
35813     
35814     process : function(file)
35815     {
35816         if(this.fireEvent('process', this, file) !== false){
35817             if(this.editable && file.type.indexOf('image') != -1){
35818                 this.fireEvent('edit', this, file);
35819                 return;
35820             }
35821
35822             this.uploadStart(file, false);
35823
35824             return;
35825         }
35826         
35827     },
35828     
35829     uploadStart : function(file, crop)
35830     {
35831         this.xhr = new XMLHttpRequest();
35832         
35833         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35834             this.arrange();
35835             return;
35836         }
35837         
35838         file.xhr = this.xhr;
35839             
35840         this.managerEl.createChild({
35841             tag : 'div',
35842             cls : 'roo-document-manager-loading',
35843             cn : [
35844                 {
35845                     tag : 'div',
35846                     tooltip : file.name,
35847                     cls : 'roo-document-manager-thumb',
35848                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35849                 }
35850             ]
35851
35852         });
35853
35854         this.xhr.open(this.method, this.url, true);
35855         
35856         var headers = {
35857             "Accept": "application/json",
35858             "Cache-Control": "no-cache",
35859             "X-Requested-With": "XMLHttpRequest"
35860         };
35861         
35862         for (var headerName in headers) {
35863             var headerValue = headers[headerName];
35864             if (headerValue) {
35865                 this.xhr.setRequestHeader(headerName, headerValue);
35866             }
35867         }
35868         
35869         var _this = this;
35870         
35871         this.xhr.onload = function()
35872         {
35873             _this.xhrOnLoad(_this.xhr);
35874         }
35875         
35876         this.xhr.onerror = function()
35877         {
35878             _this.xhrOnError(_this.xhr);
35879         }
35880         
35881         var formData = new FormData();
35882
35883         formData.append('returnHTML', 'NO');
35884         
35885         if(crop){
35886             formData.append('crop', crop);
35887         }
35888         
35889         formData.append(this.paramName, file, file.name);
35890         
35891         var options = {
35892             file : file, 
35893             manually : false
35894         };
35895         
35896         if(this.fireEvent('prepare', this, formData, options) != false){
35897             
35898             if(options.manually){
35899                 return;
35900             }
35901             
35902             this.xhr.send(formData);
35903             return;
35904         };
35905         
35906         this.uploadCancel();
35907     },
35908     
35909     uploadCancel : function()
35910     {
35911         if (this.xhr) {
35912             this.xhr.abort();
35913         }
35914         
35915         this.delegates = [];
35916         
35917         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35918             el.remove();
35919         }, this);
35920         
35921         this.arrange();
35922     },
35923     
35924     renderPreview : function(file)
35925     {
35926         if(typeof(file.target) != 'undefined' && file.target){
35927             return file;
35928         }
35929         
35930         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
35931         
35932         var previewEl = this.managerEl.createChild({
35933             tag : 'div',
35934             cls : 'roo-document-manager-preview',
35935             cn : [
35936                 {
35937                     tag : 'div',
35938                     tooltip : file[this.toolTipName],
35939                     cls : 'roo-document-manager-thumb',
35940                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
35941                 },
35942                 {
35943                     tag : 'button',
35944                     cls : 'close',
35945                     html : '<i class="fa fa-times-circle"></i>'
35946                 }
35947             ]
35948         });
35949
35950         var close = previewEl.select('button.close', true).first();
35951
35952         close.on('click', this.onRemove, this, file);
35953
35954         file.target = previewEl;
35955
35956         var image = previewEl.select('img', true).first();
35957         
35958         var _this = this;
35959         
35960         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
35961         
35962         image.on('click', this.onClick, this, file);
35963         
35964         this.fireEvent('previewrendered', this, file);
35965         
35966         return file;
35967         
35968     },
35969     
35970     onPreviewLoad : function(file, image)
35971     {
35972         if(typeof(file.target) == 'undefined' || !file.target){
35973             return;
35974         }
35975         
35976         var width = image.dom.naturalWidth || image.dom.width;
35977         var height = image.dom.naturalHeight || image.dom.height;
35978         
35979         if(!this.previewResize) {
35980             return;
35981         }
35982         
35983         if(width > height){
35984             file.target.addClass('wide');
35985             return;
35986         }
35987         
35988         file.target.addClass('tall');
35989         return;
35990         
35991     },
35992     
35993     uploadFromSource : function(file, crop)
35994     {
35995         this.xhr = new XMLHttpRequest();
35996         
35997         this.managerEl.createChild({
35998             tag : 'div',
35999             cls : 'roo-document-manager-loading',
36000             cn : [
36001                 {
36002                     tag : 'div',
36003                     tooltip : file.name,
36004                     cls : 'roo-document-manager-thumb',
36005                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
36006                 }
36007             ]
36008
36009         });
36010
36011         this.xhr.open(this.method, this.url, true);
36012         
36013         var headers = {
36014             "Accept": "application/json",
36015             "Cache-Control": "no-cache",
36016             "X-Requested-With": "XMLHttpRequest"
36017         };
36018         
36019         for (var headerName in headers) {
36020             var headerValue = headers[headerName];
36021             if (headerValue) {
36022                 this.xhr.setRequestHeader(headerName, headerValue);
36023             }
36024         }
36025         
36026         var _this = this;
36027         
36028         this.xhr.onload = function()
36029         {
36030             _this.xhrOnLoad(_this.xhr);
36031         }
36032         
36033         this.xhr.onerror = function()
36034         {
36035             _this.xhrOnError(_this.xhr);
36036         }
36037         
36038         var formData = new FormData();
36039
36040         formData.append('returnHTML', 'NO');
36041         
36042         formData.append('crop', crop);
36043         
36044         if(typeof(file.filename) != 'undefined'){
36045             formData.append('filename', file.filename);
36046         }
36047         
36048         if(typeof(file.mimetype) != 'undefined'){
36049             formData.append('mimetype', file.mimetype);
36050         }
36051         
36052         Roo.log(formData);
36053         
36054         if(this.fireEvent('prepare', this, formData) != false){
36055             this.xhr.send(formData);
36056         };
36057     }
36058 });
36059
36060 /*
36061 * Licence: LGPL
36062 */
36063
36064 /**
36065  * @class Roo.bootstrap.DocumentViewer
36066  * @extends Roo.bootstrap.Component
36067  * Bootstrap DocumentViewer class
36068  * @cfg {Boolean} showDownload (true|false) show download button (default true)
36069  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
36070  * 
36071  * @constructor
36072  * Create a new DocumentViewer
36073  * @param {Object} config The config object
36074  */
36075
36076 Roo.bootstrap.DocumentViewer = function(config){
36077     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
36078     
36079     this.addEvents({
36080         /**
36081          * @event initial
36082          * Fire after initEvent
36083          * @param {Roo.bootstrap.DocumentViewer} this
36084          */
36085         "initial" : true,
36086         /**
36087          * @event click
36088          * Fire after click
36089          * @param {Roo.bootstrap.DocumentViewer} this
36090          */
36091         "click" : true,
36092         /**
36093          * @event download
36094          * Fire after download button
36095          * @param {Roo.bootstrap.DocumentViewer} this
36096          */
36097         "download" : true,
36098         /**
36099          * @event trash
36100          * Fire after trash button
36101          * @param {Roo.bootstrap.DocumentViewer} this
36102          */
36103         "trash" : true
36104         
36105     });
36106 };
36107
36108 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
36109     
36110     showDownload : true,
36111     
36112     showTrash : true,
36113     
36114     getAutoCreate : function()
36115     {
36116         var cfg = {
36117             tag : 'div',
36118             cls : 'roo-document-viewer',
36119             cn : [
36120                 {
36121                     tag : 'div',
36122                     cls : 'roo-document-viewer-body',
36123                     cn : [
36124                         {
36125                             tag : 'div',
36126                             cls : 'roo-document-viewer-thumb',
36127                             cn : [
36128                                 {
36129                                     tag : 'img',
36130                                     cls : 'roo-document-viewer-image'
36131                                 }
36132                             ]
36133                         }
36134                     ]
36135                 },
36136                 {
36137                     tag : 'div',
36138                     cls : 'roo-document-viewer-footer',
36139                     cn : {
36140                         tag : 'div',
36141                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
36142                         cn : [
36143                             {
36144                                 tag : 'div',
36145                                 cls : 'btn-group roo-document-viewer-download',
36146                                 cn : [
36147                                     {
36148                                         tag : 'button',
36149                                         cls : 'btn btn-default',
36150                                         html : '<i class="fa fa-download"></i>'
36151                                     }
36152                                 ]
36153                             },
36154                             {
36155                                 tag : 'div',
36156                                 cls : 'btn-group roo-document-viewer-trash',
36157                                 cn : [
36158                                     {
36159                                         tag : 'button',
36160                                         cls : 'btn btn-default',
36161                                         html : '<i class="fa fa-trash"></i>'
36162                                     }
36163                                 ]
36164                             }
36165                         ]
36166                     }
36167                 }
36168             ]
36169         };
36170         
36171         return cfg;
36172     },
36173     
36174     initEvents : function()
36175     {
36176         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
36177         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36178         
36179         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
36180         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36181         
36182         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
36183         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36184         
36185         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
36186         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
36187         
36188         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
36189         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
36190         
36191         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
36192         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
36193         
36194         this.bodyEl.on('click', this.onClick, this);
36195         this.downloadBtn.on('click', this.onDownload, this);
36196         this.trashBtn.on('click', this.onTrash, this);
36197         
36198         this.downloadBtn.hide();
36199         this.trashBtn.hide();
36200         
36201         if(this.showDownload){
36202             this.downloadBtn.show();
36203         }
36204         
36205         if(this.showTrash){
36206             this.trashBtn.show();
36207         }
36208         
36209         if(!this.showDownload && !this.showTrash) {
36210             this.footerEl.hide();
36211         }
36212         
36213     },
36214     
36215     initial : function()
36216     {
36217         this.fireEvent('initial', this);
36218         
36219     },
36220     
36221     onClick : function(e)
36222     {
36223         e.preventDefault();
36224         
36225         this.fireEvent('click', this);
36226     },
36227     
36228     onDownload : function(e)
36229     {
36230         e.preventDefault();
36231         
36232         this.fireEvent('download', this);
36233     },
36234     
36235     onTrash : function(e)
36236     {
36237         e.preventDefault();
36238         
36239         this.fireEvent('trash', this);
36240     }
36241     
36242 });
36243 /*
36244  * - LGPL
36245  *
36246  * FieldLabel
36247  * 
36248  */
36249
36250 /**
36251  * @class Roo.bootstrap.form.FieldLabel
36252  * @extends Roo.bootstrap.Component
36253  * Bootstrap FieldLabel class
36254  * @cfg {String} html contents of the element
36255  * @cfg {String} tag tag of the element default label
36256  * @cfg {String} cls class of the element
36257  * @cfg {String} target label target 
36258  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
36259  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
36260  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
36261  * @cfg {String} iconTooltip default "This field is required"
36262  * @cfg {String} indicatorpos (left|right) default left
36263  * 
36264  * @constructor
36265  * Create a new FieldLabel
36266  * @param {Object} config The config object
36267  */
36268
36269 Roo.bootstrap.form.FieldLabel = function(config){
36270     Roo.bootstrap.Element.superclass.constructor.call(this, config);
36271     
36272     this.addEvents({
36273             /**
36274              * @event invalid
36275              * Fires after the field has been marked as invalid.
36276              * @param {Roo.form.FieldLabel} this
36277              * @param {String} msg The validation message
36278              */
36279             invalid : true,
36280             /**
36281              * @event valid
36282              * Fires after the field has been validated with no errors.
36283              * @param {Roo.form.FieldLabel} this
36284              */
36285             valid : true
36286         });
36287 };
36288
36289 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
36290     
36291     tag: 'label',
36292     cls: '',
36293     html: '',
36294     target: '',
36295     allowBlank : true,
36296     invalidClass : 'has-warning',
36297     validClass : 'has-success',
36298     iconTooltip : 'This field is required',
36299     indicatorpos : 'left',
36300     
36301     getAutoCreate : function(){
36302         
36303         var cls = "";
36304         if (!this.allowBlank) {
36305             cls  = "visible";
36306         }
36307         
36308         var cfg = {
36309             tag : this.tag,
36310             cls : 'roo-bootstrap-field-label ' + this.cls,
36311             for : this.target,
36312             cn : [
36313                 {
36314                     tag : 'i',
36315                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
36316                     tooltip : this.iconTooltip
36317                 },
36318                 {
36319                     tag : 'span',
36320                     html : this.html
36321                 }
36322             ] 
36323         };
36324         
36325         if(this.indicatorpos == 'right'){
36326             var cfg = {
36327                 tag : this.tag,
36328                 cls : 'roo-bootstrap-field-label ' + this.cls,
36329                 for : this.target,
36330                 cn : [
36331                     {
36332                         tag : 'span',
36333                         html : this.html
36334                     },
36335                     {
36336                         tag : 'i',
36337                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
36338                         tooltip : this.iconTooltip
36339                     }
36340                 ] 
36341             };
36342         }
36343         
36344         return cfg;
36345     },
36346     
36347     initEvents: function() 
36348     {
36349         Roo.bootstrap.Element.superclass.initEvents.call(this);
36350         
36351         this.indicator = this.indicatorEl();
36352         
36353         if(this.indicator){
36354             this.indicator.removeClass('visible');
36355             this.indicator.addClass('invisible');
36356         }
36357         
36358         Roo.bootstrap.form.FieldLabel.register(this);
36359     },
36360     
36361     indicatorEl : function()
36362     {
36363         var indicator = this.el.select('i.roo-required-indicator',true).first();
36364         
36365         if(!indicator){
36366             return false;
36367         }
36368         
36369         return indicator;
36370         
36371     },
36372     
36373     /**
36374      * Mark this field as valid
36375      */
36376     markValid : function()
36377     {
36378         if(this.indicator){
36379             this.indicator.removeClass('visible');
36380             this.indicator.addClass('invisible');
36381         }
36382         if (Roo.bootstrap.version == 3) {
36383             this.el.removeClass(this.invalidClass);
36384             this.el.addClass(this.validClass);
36385         } else {
36386             this.el.removeClass('is-invalid');
36387             this.el.addClass('is-valid');
36388         }
36389         
36390         
36391         this.fireEvent('valid', this);
36392     },
36393     
36394     /**
36395      * Mark this field as invalid
36396      * @param {String} msg The validation message
36397      */
36398     markInvalid : function(msg)
36399     {
36400         if(this.indicator){
36401             this.indicator.removeClass('invisible');
36402             this.indicator.addClass('visible');
36403         }
36404           if (Roo.bootstrap.version == 3) {
36405             this.el.removeClass(this.validClass);
36406             this.el.addClass(this.invalidClass);
36407         } else {
36408             this.el.removeClass('is-valid');
36409             this.el.addClass('is-invalid');
36410         }
36411         
36412         
36413         this.fireEvent('invalid', this, msg);
36414     }
36415     
36416    
36417 });
36418
36419 Roo.apply(Roo.bootstrap.form.FieldLabel, {
36420     
36421     groups: {},
36422     
36423      /**
36424     * register a FieldLabel Group
36425     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
36426     */
36427     register : function(label)
36428     {
36429         if(this.groups.hasOwnProperty(label.target)){
36430             return;
36431         }
36432      
36433         this.groups[label.target] = label;
36434         
36435     },
36436     /**
36437     * fetch a FieldLabel Group based on the target
36438     * @param {string} target
36439     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
36440     */
36441     get: function(target) {
36442         if (typeof(this.groups[target]) == 'undefined') {
36443             return false;
36444         }
36445         
36446         return this.groups[target] ;
36447     }
36448 });
36449
36450  
36451
36452  /*
36453  * - LGPL
36454  *
36455  * page DateSplitField.
36456  * 
36457  */
36458
36459
36460 /**
36461  * @class Roo.bootstrap.form.DateSplitField
36462  * @extends Roo.bootstrap.Component
36463  * Bootstrap DateSplitField class
36464  * @cfg {string} fieldLabel - the label associated
36465  * @cfg {Number} labelWidth set the width of label (0-12)
36466  * @cfg {String} labelAlign (top|left)
36467  * @cfg {Boolean} dayAllowBlank (true|false) default false
36468  * @cfg {Boolean} monthAllowBlank (true|false) default false
36469  * @cfg {Boolean} yearAllowBlank (true|false) default false
36470  * @cfg {string} dayPlaceholder 
36471  * @cfg {string} monthPlaceholder
36472  * @cfg {string} yearPlaceholder
36473  * @cfg {string} dayFormat default 'd'
36474  * @cfg {string} monthFormat default 'm'
36475  * @cfg {string} yearFormat default 'Y'
36476  * @cfg {Number} labellg set the width of label (1-12)
36477  * @cfg {Number} labelmd set the width of label (1-12)
36478  * @cfg {Number} labelsm set the width of label (1-12)
36479  * @cfg {Number} labelxs set the width of label (1-12)
36480
36481  *     
36482  * @constructor
36483  * Create a new DateSplitField
36484  * @param {Object} config The config object
36485  */
36486
36487 Roo.bootstrap.form.DateSplitField = function(config){
36488     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
36489     
36490     this.addEvents({
36491         // raw events
36492          /**
36493          * @event years
36494          * getting the data of years
36495          * @param {Roo.bootstrap.form.DateSplitField} this
36496          * @param {Object} years
36497          */
36498         "years" : true,
36499         /**
36500          * @event days
36501          * getting the data of days
36502          * @param {Roo.bootstrap.form.DateSplitField} this
36503          * @param {Object} days
36504          */
36505         "days" : true,
36506         /**
36507          * @event invalid
36508          * Fires after the field has been marked as invalid.
36509          * @param {Roo.form.Field} this
36510          * @param {String} msg The validation message
36511          */
36512         invalid : true,
36513        /**
36514          * @event valid
36515          * Fires after the field has been validated with no errors.
36516          * @param {Roo.form.Field} this
36517          */
36518         valid : true
36519     });
36520 };
36521
36522 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
36523     
36524     fieldLabel : '',
36525     labelAlign : 'top',
36526     labelWidth : 3,
36527     dayAllowBlank : false,
36528     monthAllowBlank : false,
36529     yearAllowBlank : false,
36530     dayPlaceholder : '',
36531     monthPlaceholder : '',
36532     yearPlaceholder : '',
36533     dayFormat : 'd',
36534     monthFormat : 'm',
36535     yearFormat : 'Y',
36536     isFormField : true,
36537     labellg : 0,
36538     labelmd : 0,
36539     labelsm : 0,
36540     labelxs : 0,
36541     
36542     getAutoCreate : function()
36543     {
36544         var cfg = {
36545             tag : 'div',
36546             cls : 'row roo-date-split-field-group',
36547             cn : [
36548                 {
36549                     tag : 'input',
36550                     type : 'hidden',
36551                     cls : 'form-hidden-field roo-date-split-field-group-value',
36552                     name : this.name
36553                 }
36554             ]
36555         };
36556         
36557         var labelCls = 'col-md-12';
36558         var contentCls = 'col-md-4';
36559         
36560         if(this.fieldLabel){
36561             
36562             var label = {
36563                 tag : 'div',
36564                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
36565                 cn : [
36566                     {
36567                         tag : 'label',
36568                         html : this.fieldLabel
36569                     }
36570                 ]
36571             };
36572             
36573             if(this.labelAlign == 'left'){
36574             
36575                 if(this.labelWidth > 12){
36576                     label.style = "width: " + this.labelWidth + 'px';
36577                 }
36578
36579                 if(this.labelWidth < 13 && this.labelmd == 0){
36580                     this.labelmd = this.labelWidth;
36581                 }
36582
36583                 if(this.labellg > 0){
36584                     labelCls = ' col-lg-' + this.labellg;
36585                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
36586                 }
36587
36588                 if(this.labelmd > 0){
36589                     labelCls = ' col-md-' + this.labelmd;
36590                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
36591                 }
36592
36593                 if(this.labelsm > 0){
36594                     labelCls = ' col-sm-' + this.labelsm;
36595                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
36596                 }
36597
36598                 if(this.labelxs > 0){
36599                     labelCls = ' col-xs-' + this.labelxs;
36600                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
36601                 }
36602             }
36603             
36604             label.cls += ' ' + labelCls;
36605             
36606             cfg.cn.push(label);
36607         }
36608         
36609         Roo.each(['day', 'month', 'year'], function(t){
36610             cfg.cn.push({
36611                 tag : 'div',
36612                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
36613             });
36614         }, this);
36615         
36616         return cfg;
36617     },
36618     
36619     inputEl: function ()
36620     {
36621         return this.el.select('.roo-date-split-field-group-value', true).first();
36622     },
36623     
36624     onRender : function(ct, position) 
36625     {
36626         var _this = this;
36627         
36628         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
36629         
36630         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
36631         
36632         this.dayField = new Roo.bootstrap.form.ComboBox({
36633             allowBlank : this.dayAllowBlank,
36634             alwaysQuery : true,
36635             displayField : 'value',
36636             editable : false,
36637             fieldLabel : '',
36638             forceSelection : true,
36639             mode : 'local',
36640             placeholder : this.dayPlaceholder,
36641             selectOnFocus : true,
36642             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36643             triggerAction : 'all',
36644             typeAhead : true,
36645             valueField : 'value',
36646             store : new Roo.data.SimpleStore({
36647                 data : (function() {    
36648                     var days = [];
36649                     _this.fireEvent('days', _this, days);
36650                     return days;
36651                 })(),
36652                 fields : [ 'value' ]
36653             }),
36654             listeners : {
36655                 select : function (_self, record, index)
36656                 {
36657                     _this.setValue(_this.getValue());
36658                 }
36659             }
36660         });
36661
36662         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
36663         
36664         this.monthField = new Roo.bootstrap.form.MonthField({
36665             after : '<i class=\"fa fa-calendar\"></i>',
36666             allowBlank : this.monthAllowBlank,
36667             placeholder : this.monthPlaceholder,
36668             readOnly : true,
36669             listeners : {
36670                 render : function (_self)
36671                 {
36672                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
36673                         e.preventDefault();
36674                         _self.focus();
36675                     });
36676                 },
36677                 select : function (_self, oldvalue, newvalue)
36678                 {
36679                     _this.setValue(_this.getValue());
36680                 }
36681             }
36682         });
36683         
36684         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
36685         
36686         this.yearField = new Roo.bootstrap.form.ComboBox({
36687             allowBlank : this.yearAllowBlank,
36688             alwaysQuery : true,
36689             displayField : 'value',
36690             editable : false,
36691             fieldLabel : '',
36692             forceSelection : true,
36693             mode : 'local',
36694             placeholder : this.yearPlaceholder,
36695             selectOnFocus : true,
36696             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36697             triggerAction : 'all',
36698             typeAhead : true,
36699             valueField : 'value',
36700             store : new Roo.data.SimpleStore({
36701                 data : (function() {
36702                     var years = [];
36703                     _this.fireEvent('years', _this, years);
36704                     return years;
36705                 })(),
36706                 fields : [ 'value' ]
36707             }),
36708             listeners : {
36709                 select : function (_self, record, index)
36710                 {
36711                     _this.setValue(_this.getValue());
36712                 }
36713             }
36714         });
36715
36716         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
36717     },
36718     
36719     setValue : function(v, format)
36720     {
36721         this.inputEl.dom.value = v;
36722         
36723         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
36724         
36725         var d = Date.parseDate(v, f);
36726         
36727         if(!d){
36728             this.validate();
36729             return;
36730         }
36731         
36732         this.setDay(d.format(this.dayFormat));
36733         this.setMonth(d.format(this.monthFormat));
36734         this.setYear(d.format(this.yearFormat));
36735         
36736         this.validate();
36737         
36738         return;
36739     },
36740     
36741     setDay : function(v)
36742     {
36743         this.dayField.setValue(v);
36744         this.inputEl.dom.value = this.getValue();
36745         this.validate();
36746         return;
36747     },
36748     
36749     setMonth : function(v)
36750     {
36751         this.monthField.setValue(v, true);
36752         this.inputEl.dom.value = this.getValue();
36753         this.validate();
36754         return;
36755     },
36756     
36757     setYear : function(v)
36758     {
36759         this.yearField.setValue(v);
36760         this.inputEl.dom.value = this.getValue();
36761         this.validate();
36762         return;
36763     },
36764     
36765     getDay : function()
36766     {
36767         return this.dayField.getValue();
36768     },
36769     
36770     getMonth : function()
36771     {
36772         return this.monthField.getValue();
36773     },
36774     
36775     getYear : function()
36776     {
36777         return this.yearField.getValue();
36778     },
36779     
36780     getValue : function()
36781     {
36782         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
36783         
36784         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
36785         
36786         return date;
36787     },
36788     
36789     reset : function()
36790     {
36791         this.setDay('');
36792         this.setMonth('');
36793         this.setYear('');
36794         this.inputEl.dom.value = '';
36795         this.validate();
36796         return;
36797     },
36798     
36799     validate : function()
36800     {
36801         var d = this.dayField.validate();
36802         var m = this.monthField.validate();
36803         var y = this.yearField.validate();
36804         
36805         var valid = true;
36806         
36807         if(
36808                 (!this.dayAllowBlank && !d) ||
36809                 (!this.monthAllowBlank && !m) ||
36810                 (!this.yearAllowBlank && !y)
36811         ){
36812             valid = false;
36813         }
36814         
36815         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
36816             return valid;
36817         }
36818         
36819         if(valid){
36820             this.markValid();
36821             return valid;
36822         }
36823         
36824         this.markInvalid();
36825         
36826         return valid;
36827     },
36828     
36829     markValid : function()
36830     {
36831         
36832         var label = this.el.select('label', true).first();
36833         var icon = this.el.select('i.fa-star', true).first();
36834
36835         if(label && icon){
36836             icon.remove();
36837         }
36838         
36839         this.fireEvent('valid', this);
36840     },
36841     
36842      /**
36843      * Mark this field as invalid
36844      * @param {String} msg The validation message
36845      */
36846     markInvalid : function(msg)
36847     {
36848         
36849         var label = this.el.select('label', true).first();
36850         var icon = this.el.select('i.fa-star', true).first();
36851
36852         if(label && !icon){
36853             this.el.select('.roo-date-split-field-label', true).createChild({
36854                 tag : 'i',
36855                 cls : 'text-danger fa fa-lg fa-star',
36856                 tooltip : 'This field is required',
36857                 style : 'margin-right:5px;'
36858             }, label, true);
36859         }
36860         
36861         this.fireEvent('invalid', this, msg);
36862     },
36863     
36864     clearInvalid : function()
36865     {
36866         var label = this.el.select('label', true).first();
36867         var icon = this.el.select('i.fa-star', true).first();
36868
36869         if(label && icon){
36870             icon.remove();
36871         }
36872         
36873         this.fireEvent('valid', this);
36874     },
36875     
36876     getName: function()
36877     {
36878         return this.name;
36879     }
36880     
36881 });
36882
36883  
36884
36885 /**
36886  * @class Roo.bootstrap.LayoutMasonry
36887  * @extends Roo.bootstrap.Component
36888  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
36889  * Bootstrap Layout Masonry class
36890  *
36891  * This is based on 
36892  * http://masonry.desandro.com
36893  *
36894  * The idea is to render all the bricks based on vertical width...
36895  *
36896  * The original code extends 'outlayer' - we might need to use that....
36897
36898  * @constructor
36899  * Create a new Element
36900  * @param {Object} config The config object
36901  */
36902
36903 Roo.bootstrap.LayoutMasonry = function(config){
36904     
36905     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
36906     
36907     this.bricks = [];
36908     
36909     Roo.bootstrap.LayoutMasonry.register(this);
36910     
36911     this.addEvents({
36912         // raw events
36913         /**
36914          * @event layout
36915          * Fire after layout the items
36916          * @param {Roo.bootstrap.LayoutMasonry} this
36917          * @param {Roo.EventObject} e
36918          */
36919         "layout" : true
36920     });
36921     
36922 };
36923
36924 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
36925     
36926     /**
36927      * @cfg {Boolean} isLayoutInstant = no animation?
36928      */   
36929     isLayoutInstant : false, // needed?
36930    
36931     /**
36932      * @cfg {Number} boxWidth  width of the columns
36933      */   
36934     boxWidth : 450,
36935     
36936       /**
36937      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
36938      */   
36939     boxHeight : 0,
36940     
36941     /**
36942      * @cfg {Number} padWidth padding below box..
36943      */   
36944     padWidth : 10, 
36945     
36946     /**
36947      * @cfg {Number} gutter gutter width..
36948      */   
36949     gutter : 10,
36950     
36951      /**
36952      * @cfg {Number} maxCols maximum number of columns
36953      */   
36954     
36955     maxCols: 0,
36956     
36957     /**
36958      * @cfg {Boolean} isAutoInitial defalut true
36959      */   
36960     isAutoInitial : true, 
36961     
36962     containerWidth: 0,
36963     
36964     /**
36965      * @cfg {Boolean} isHorizontal defalut false
36966      */   
36967     isHorizontal : false, 
36968
36969     currentSize : null,
36970     
36971     tag: 'div',
36972     
36973     cls: '',
36974     
36975     bricks: null, //CompositeElement
36976     
36977     cols : 1,
36978     
36979     _isLayoutInited : false,
36980     
36981 //    isAlternative : false, // only use for vertical layout...
36982     
36983     /**
36984      * @cfg {Number} alternativePadWidth padding below box..
36985      */   
36986     alternativePadWidth : 50,
36987     
36988     selectedBrick : [],
36989     
36990     getAutoCreate : function(){
36991         
36992         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
36993         
36994         var cfg = {
36995             tag: this.tag,
36996             cls: 'blog-masonary-wrapper ' + this.cls,
36997             cn : {
36998                 cls : 'mas-boxes masonary'
36999             }
37000         };
37001         
37002         return cfg;
37003     },
37004     
37005     getChildContainer: function( )
37006     {
37007         if (this.boxesEl) {
37008             return this.boxesEl;
37009         }
37010         
37011         this.boxesEl = this.el.select('.mas-boxes').first();
37012         
37013         return this.boxesEl;
37014     },
37015     
37016     
37017     initEvents : function()
37018     {
37019         var _this = this;
37020         
37021         if(this.isAutoInitial){
37022             Roo.log('hook children rendered');
37023             this.on('childrenrendered', function() {
37024                 Roo.log('children rendered');
37025                 _this.initial();
37026             } ,this);
37027         }
37028     },
37029     
37030     initial : function()
37031     {
37032         this.selectedBrick = [];
37033         
37034         this.currentSize = this.el.getBox(true);
37035         
37036         Roo.EventManager.onWindowResize(this.resize, this); 
37037
37038         if(!this.isAutoInitial){
37039             this.layout();
37040             return;
37041         }
37042         
37043         this.layout();
37044         
37045         return;
37046         //this.layout.defer(500,this);
37047         
37048     },
37049     
37050     resize : function()
37051     {
37052         var cs = this.el.getBox(true);
37053         
37054         if (
37055                 this.currentSize.width == cs.width && 
37056                 this.currentSize.x == cs.x && 
37057                 this.currentSize.height == cs.height && 
37058                 this.currentSize.y == cs.y 
37059         ) {
37060             Roo.log("no change in with or X or Y");
37061             return;
37062         }
37063         
37064         this.currentSize = cs;
37065         
37066         this.layout();
37067         
37068     },
37069     
37070     layout : function()
37071     {   
37072         this._resetLayout();
37073         
37074         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
37075         
37076         this.layoutItems( isInstant );
37077       
37078         this._isLayoutInited = true;
37079         
37080         this.fireEvent('layout', this);
37081         
37082     },
37083     
37084     _resetLayout : function()
37085     {
37086         if(this.isHorizontal){
37087             this.horizontalMeasureColumns();
37088             return;
37089         }
37090         
37091         this.verticalMeasureColumns();
37092         
37093     },
37094     
37095     verticalMeasureColumns : function()
37096     {
37097         this.getContainerWidth();
37098         
37099 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
37100 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
37101 //            return;
37102 //        }
37103         
37104         var boxWidth = this.boxWidth + this.padWidth;
37105         
37106         if(this.containerWidth < this.boxWidth){
37107             boxWidth = this.containerWidth
37108         }
37109         
37110         var containerWidth = this.containerWidth;
37111         
37112         var cols = Math.floor(containerWidth / boxWidth);
37113         
37114         this.cols = Math.max( cols, 1 );
37115         
37116         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
37117         
37118         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
37119         
37120         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
37121         
37122         this.colWidth = boxWidth + avail - this.padWidth;
37123         
37124         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
37125         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
37126     },
37127     
37128     horizontalMeasureColumns : function()
37129     {
37130         this.getContainerWidth();
37131         
37132         var boxWidth = this.boxWidth;
37133         
37134         if(this.containerWidth < boxWidth){
37135             boxWidth = this.containerWidth;
37136         }
37137         
37138         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
37139         
37140         this.el.setHeight(boxWidth);
37141         
37142     },
37143     
37144     getContainerWidth : function()
37145     {
37146         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
37147     },
37148     
37149     layoutItems : function( isInstant )
37150     {
37151         Roo.log(this.bricks);
37152         
37153         var items = Roo.apply([], this.bricks);
37154         
37155         if(this.isHorizontal){
37156             this._horizontalLayoutItems( items , isInstant );
37157             return;
37158         }
37159         
37160 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
37161 //            this._verticalAlternativeLayoutItems( items , isInstant );
37162 //            return;
37163 //        }
37164         
37165         this._verticalLayoutItems( items , isInstant );
37166         
37167     },
37168     
37169     _verticalLayoutItems : function ( items , isInstant)
37170     {
37171         if ( !items || !items.length ) {
37172             return;
37173         }
37174         
37175         var standard = [
37176             ['xs', 'xs', 'xs', 'tall'],
37177             ['xs', 'xs', 'tall'],
37178             ['xs', 'xs', 'sm'],
37179             ['xs', 'xs', 'xs'],
37180             ['xs', 'tall'],
37181             ['xs', 'sm'],
37182             ['xs', 'xs'],
37183             ['xs'],
37184             
37185             ['sm', 'xs', 'xs'],
37186             ['sm', 'xs'],
37187             ['sm'],
37188             
37189             ['tall', 'xs', 'xs', 'xs'],
37190             ['tall', 'xs', 'xs'],
37191             ['tall', 'xs'],
37192             ['tall']
37193             
37194         ];
37195         
37196         var queue = [];
37197         
37198         var boxes = [];
37199         
37200         var box = [];
37201         
37202         Roo.each(items, function(item, k){
37203             
37204             switch (item.size) {
37205                 // these layouts take up a full box,
37206                 case 'md' :
37207                 case 'md-left' :
37208                 case 'md-right' :
37209                 case 'wide' :
37210                     
37211                     if(box.length){
37212                         boxes.push(box);
37213                         box = [];
37214                     }
37215                     
37216                     boxes.push([item]);
37217                     
37218                     break;
37219                     
37220                 case 'xs' :
37221                 case 'sm' :
37222                 case 'tall' :
37223                     
37224                     box.push(item);
37225                     
37226                     break;
37227                 default :
37228                     break;
37229                     
37230             }
37231             
37232         }, this);
37233         
37234         if(box.length){
37235             boxes.push(box);
37236             box = [];
37237         }
37238         
37239         var filterPattern = function(box, length)
37240         {
37241             if(!box.length){
37242                 return;
37243             }
37244             
37245             var match = false;
37246             
37247             var pattern = box.slice(0, length);
37248             
37249             var format = [];
37250             
37251             Roo.each(pattern, function(i){
37252                 format.push(i.size);
37253             }, this);
37254             
37255             Roo.each(standard, function(s){
37256                 
37257                 if(String(s) != String(format)){
37258                     return;
37259                 }
37260                 
37261                 match = true;
37262                 return false;
37263                 
37264             }, this);
37265             
37266             if(!match && length == 1){
37267                 return;
37268             }
37269             
37270             if(!match){
37271                 filterPattern(box, length - 1);
37272                 return;
37273             }
37274                 
37275             queue.push(pattern);
37276
37277             box = box.slice(length, box.length);
37278
37279             filterPattern(box, 4);
37280
37281             return;
37282             
37283         }
37284         
37285         Roo.each(boxes, function(box, k){
37286             
37287             if(!box.length){
37288                 return;
37289             }
37290             
37291             if(box.length == 1){
37292                 queue.push(box);
37293                 return;
37294             }
37295             
37296             filterPattern(box, 4);
37297             
37298         }, this);
37299         
37300         this._processVerticalLayoutQueue( queue, isInstant );
37301         
37302     },
37303     
37304 //    _verticalAlternativeLayoutItems : function( items , isInstant )
37305 //    {
37306 //        if ( !items || !items.length ) {
37307 //            return;
37308 //        }
37309 //
37310 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
37311 //        
37312 //    },
37313     
37314     _horizontalLayoutItems : function ( items , isInstant)
37315     {
37316         if ( !items || !items.length || items.length < 3) {
37317             return;
37318         }
37319         
37320         items.reverse();
37321         
37322         var eItems = items.slice(0, 3);
37323         
37324         items = items.slice(3, items.length);
37325         
37326         var standard = [
37327             ['xs', 'xs', 'xs', 'wide'],
37328             ['xs', 'xs', 'wide'],
37329             ['xs', 'xs', 'sm'],
37330             ['xs', 'xs', 'xs'],
37331             ['xs', 'wide'],
37332             ['xs', 'sm'],
37333             ['xs', 'xs'],
37334             ['xs'],
37335             
37336             ['sm', 'xs', 'xs'],
37337             ['sm', 'xs'],
37338             ['sm'],
37339             
37340             ['wide', 'xs', 'xs', 'xs'],
37341             ['wide', 'xs', 'xs'],
37342             ['wide', 'xs'],
37343             ['wide'],
37344             
37345             ['wide-thin']
37346         ];
37347         
37348         var queue = [];
37349         
37350         var boxes = [];
37351         
37352         var box = [];
37353         
37354         Roo.each(items, function(item, k){
37355             
37356             switch (item.size) {
37357                 case 'md' :
37358                 case 'md-left' :
37359                 case 'md-right' :
37360                 case 'tall' :
37361                     
37362                     if(box.length){
37363                         boxes.push(box);
37364                         box = [];
37365                     }
37366                     
37367                     boxes.push([item]);
37368                     
37369                     break;
37370                     
37371                 case 'xs' :
37372                 case 'sm' :
37373                 case 'wide' :
37374                 case 'wide-thin' :
37375                     
37376                     box.push(item);
37377                     
37378                     break;
37379                 default :
37380                     break;
37381                     
37382             }
37383             
37384         }, this);
37385         
37386         if(box.length){
37387             boxes.push(box);
37388             box = [];
37389         }
37390         
37391         var filterPattern = function(box, length)
37392         {
37393             if(!box.length){
37394                 return;
37395             }
37396             
37397             var match = false;
37398             
37399             var pattern = box.slice(0, length);
37400             
37401             var format = [];
37402             
37403             Roo.each(pattern, function(i){
37404                 format.push(i.size);
37405             }, this);
37406             
37407             Roo.each(standard, function(s){
37408                 
37409                 if(String(s) != String(format)){
37410                     return;
37411                 }
37412                 
37413                 match = true;
37414                 return false;
37415                 
37416             }, this);
37417             
37418             if(!match && length == 1){
37419                 return;
37420             }
37421             
37422             if(!match){
37423                 filterPattern(box, length - 1);
37424                 return;
37425             }
37426                 
37427             queue.push(pattern);
37428
37429             box = box.slice(length, box.length);
37430
37431             filterPattern(box, 4);
37432
37433             return;
37434             
37435         }
37436         
37437         Roo.each(boxes, function(box, k){
37438             
37439             if(!box.length){
37440                 return;
37441             }
37442             
37443             if(box.length == 1){
37444                 queue.push(box);
37445                 return;
37446             }
37447             
37448             filterPattern(box, 4);
37449             
37450         }, this);
37451         
37452         
37453         var prune = [];
37454         
37455         var pos = this.el.getBox(true);
37456         
37457         var minX = pos.x;
37458         
37459         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37460         
37461         var hit_end = false;
37462         
37463         Roo.each(queue, function(box){
37464             
37465             if(hit_end){
37466                 
37467                 Roo.each(box, function(b){
37468                 
37469                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37470                     b.el.hide();
37471
37472                 }, this);
37473
37474                 return;
37475             }
37476             
37477             var mx = 0;
37478             
37479             Roo.each(box, function(b){
37480                 
37481                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37482                 b.el.show();
37483
37484                 mx = Math.max(mx, b.x);
37485                 
37486             }, this);
37487             
37488             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
37489             
37490             if(maxX < minX){
37491                 
37492                 Roo.each(box, function(b){
37493                 
37494                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37495                     b.el.hide();
37496                     
37497                 }, this);
37498                 
37499                 hit_end = true;
37500                 
37501                 return;
37502             }
37503             
37504             prune.push(box);
37505             
37506         }, this);
37507         
37508         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
37509     },
37510     
37511     /** Sets position of item in DOM
37512     * @param {Element} item
37513     * @param {Number} x - horizontal position
37514     * @param {Number} y - vertical position
37515     * @param {Boolean} isInstant - disables transitions
37516     */
37517     _processVerticalLayoutQueue : function( queue, isInstant )
37518     {
37519         var pos = this.el.getBox(true);
37520         var x = pos.x;
37521         var y = pos.y;
37522         var maxY = [];
37523         
37524         for (var i = 0; i < this.cols; i++){
37525             maxY[i] = pos.y;
37526         }
37527         
37528         Roo.each(queue, function(box, k){
37529             
37530             var col = k % this.cols;
37531             
37532             Roo.each(box, function(b,kk){
37533                 
37534                 b.el.position('absolute');
37535                 
37536                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37537                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37538                 
37539                 if(b.size == 'md-left' || b.size == 'md-right'){
37540                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37541                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37542                 }
37543                 
37544                 b.el.setWidth(width);
37545                 b.el.setHeight(height);
37546                 // iframe?
37547                 b.el.select('iframe',true).setSize(width,height);
37548                 
37549             }, this);
37550             
37551             for (var i = 0; i < this.cols; i++){
37552                 
37553                 if(maxY[i] < maxY[col]){
37554                     col = i;
37555                     continue;
37556                 }
37557                 
37558                 col = Math.min(col, i);
37559                 
37560             }
37561             
37562             x = pos.x + col * (this.colWidth + this.padWidth);
37563             
37564             y = maxY[col];
37565             
37566             var positions = [];
37567             
37568             switch (box.length){
37569                 case 1 :
37570                     positions = this.getVerticalOneBoxColPositions(x, y, box);
37571                     break;
37572                 case 2 :
37573                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
37574                     break;
37575                 case 3 :
37576                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
37577                     break;
37578                 case 4 :
37579                     positions = this.getVerticalFourBoxColPositions(x, y, box);
37580                     break;
37581                 default :
37582                     break;
37583             }
37584             
37585             Roo.each(box, function(b,kk){
37586                 
37587                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37588                 
37589                 var sz = b.el.getSize();
37590                 
37591                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
37592                 
37593             }, this);
37594             
37595         }, this);
37596         
37597         var mY = 0;
37598         
37599         for (var i = 0; i < this.cols; i++){
37600             mY = Math.max(mY, maxY[i]);
37601         }
37602         
37603         this.el.setHeight(mY - pos.y);
37604         
37605     },
37606     
37607 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
37608 //    {
37609 //        var pos = this.el.getBox(true);
37610 //        var x = pos.x;
37611 //        var y = pos.y;
37612 //        var maxX = pos.right;
37613 //        
37614 //        var maxHeight = 0;
37615 //        
37616 //        Roo.each(items, function(item, k){
37617 //            
37618 //            var c = k % 2;
37619 //            
37620 //            item.el.position('absolute');
37621 //                
37622 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
37623 //
37624 //            item.el.setWidth(width);
37625 //
37626 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
37627 //
37628 //            item.el.setHeight(height);
37629 //            
37630 //            if(c == 0){
37631 //                item.el.setXY([x, y], isInstant ? false : true);
37632 //            } else {
37633 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
37634 //            }
37635 //            
37636 //            y = y + height + this.alternativePadWidth;
37637 //            
37638 //            maxHeight = maxHeight + height + this.alternativePadWidth;
37639 //            
37640 //        }, this);
37641 //        
37642 //        this.el.setHeight(maxHeight);
37643 //        
37644 //    },
37645     
37646     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
37647     {
37648         var pos = this.el.getBox(true);
37649         
37650         var minX = pos.x;
37651         var minY = pos.y;
37652         
37653         var maxX = pos.right;
37654         
37655         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
37656         
37657         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37658         
37659         Roo.each(queue, function(box, k){
37660             
37661             Roo.each(box, function(b, kk){
37662                 
37663                 b.el.position('absolute');
37664                 
37665                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37666                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37667                 
37668                 if(b.size == 'md-left' || b.size == 'md-right'){
37669                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37670                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37671                 }
37672                 
37673                 b.el.setWidth(width);
37674                 b.el.setHeight(height);
37675                 
37676             }, this);
37677             
37678             if(!box.length){
37679                 return;
37680             }
37681             
37682             var positions = [];
37683             
37684             switch (box.length){
37685                 case 1 :
37686                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
37687                     break;
37688                 case 2 :
37689                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
37690                     break;
37691                 case 3 :
37692                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
37693                     break;
37694                 case 4 :
37695                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
37696                     break;
37697                 default :
37698                     break;
37699             }
37700             
37701             Roo.each(box, function(b,kk){
37702                 
37703                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37704                 
37705                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
37706                 
37707             }, this);
37708             
37709         }, this);
37710         
37711     },
37712     
37713     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
37714     {
37715         Roo.each(eItems, function(b,k){
37716             
37717             b.size = (k == 0) ? 'sm' : 'xs';
37718             b.x = (k == 0) ? 2 : 1;
37719             b.y = (k == 0) ? 2 : 1;
37720             
37721             b.el.position('absolute');
37722             
37723             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37724                 
37725             b.el.setWidth(width);
37726             
37727             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37728             
37729             b.el.setHeight(height);
37730             
37731         }, this);
37732
37733         var positions = [];
37734         
37735         positions.push({
37736             x : maxX - this.unitWidth * 2 - this.gutter,
37737             y : minY
37738         });
37739         
37740         positions.push({
37741             x : maxX - this.unitWidth,
37742             y : minY + (this.unitWidth + this.gutter) * 2
37743         });
37744         
37745         positions.push({
37746             x : maxX - this.unitWidth * 3 - this.gutter * 2,
37747             y : minY
37748         });
37749         
37750         Roo.each(eItems, function(b,k){
37751             
37752             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
37753
37754         }, this);
37755         
37756     },
37757     
37758     getVerticalOneBoxColPositions : function(x, y, box)
37759     {
37760         var pos = [];
37761         
37762         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
37763         
37764         if(box[0].size == 'md-left'){
37765             rand = 0;
37766         }
37767         
37768         if(box[0].size == 'md-right'){
37769             rand = 1;
37770         }
37771         
37772         pos.push({
37773             x : x + (this.unitWidth + this.gutter) * rand,
37774             y : y
37775         });
37776         
37777         return pos;
37778     },
37779     
37780     getVerticalTwoBoxColPositions : function(x, y, box)
37781     {
37782         var pos = [];
37783         
37784         if(box[0].size == 'xs'){
37785             
37786             pos.push({
37787                 x : x,
37788                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
37789             });
37790
37791             pos.push({
37792                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
37793                 y : y
37794             });
37795             
37796             return pos;
37797             
37798         }
37799         
37800         pos.push({
37801             x : x,
37802             y : y
37803         });
37804
37805         pos.push({
37806             x : x + (this.unitWidth + this.gutter) * 2,
37807             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
37808         });
37809         
37810         return pos;
37811         
37812     },
37813     
37814     getVerticalThreeBoxColPositions : function(x, y, box)
37815     {
37816         var pos = [];
37817         
37818         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37819             
37820             pos.push({
37821                 x : x,
37822                 y : y
37823             });
37824
37825             pos.push({
37826                 x : x + (this.unitWidth + this.gutter) * 1,
37827                 y : y
37828             });
37829             
37830             pos.push({
37831                 x : x + (this.unitWidth + this.gutter) * 2,
37832                 y : y
37833             });
37834             
37835             return pos;
37836             
37837         }
37838         
37839         if(box[0].size == 'xs' && box[1].size == 'xs'){
37840             
37841             pos.push({
37842                 x : x,
37843                 y : y
37844             });
37845
37846             pos.push({
37847                 x : x,
37848                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
37849             });
37850             
37851             pos.push({
37852                 x : x + (this.unitWidth + this.gutter) * 1,
37853                 y : y
37854             });
37855             
37856             return pos;
37857             
37858         }
37859         
37860         pos.push({
37861             x : x,
37862             y : y
37863         });
37864
37865         pos.push({
37866             x : x + (this.unitWidth + this.gutter) * 2,
37867             y : y
37868         });
37869
37870         pos.push({
37871             x : x + (this.unitWidth + this.gutter) * 2,
37872             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
37873         });
37874             
37875         return pos;
37876         
37877     },
37878     
37879     getVerticalFourBoxColPositions : function(x, y, box)
37880     {
37881         var pos = [];
37882         
37883         if(box[0].size == 'xs'){
37884             
37885             pos.push({
37886                 x : x,
37887                 y : y
37888             });
37889
37890             pos.push({
37891                 x : x,
37892                 y : y + (this.unitHeight + this.gutter) * 1
37893             });
37894             
37895             pos.push({
37896                 x : x,
37897                 y : y + (this.unitHeight + this.gutter) * 2
37898             });
37899             
37900             pos.push({
37901                 x : x + (this.unitWidth + this.gutter) * 1,
37902                 y : y
37903             });
37904             
37905             return pos;
37906             
37907         }
37908         
37909         pos.push({
37910             x : x,
37911             y : y
37912         });
37913
37914         pos.push({
37915             x : x + (this.unitWidth + this.gutter) * 2,
37916             y : y
37917         });
37918
37919         pos.push({
37920             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
37921             y : y + (this.unitHeight + this.gutter) * 1
37922         });
37923
37924         pos.push({
37925             x : x + (this.unitWidth + this.gutter) * 2,
37926             y : y + (this.unitWidth + this.gutter) * 2
37927         });
37928
37929         return pos;
37930         
37931     },
37932     
37933     getHorizontalOneBoxColPositions : function(maxX, minY, box)
37934     {
37935         var pos = [];
37936         
37937         if(box[0].size == 'md-left'){
37938             pos.push({
37939                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37940                 y : minY
37941             });
37942             
37943             return pos;
37944         }
37945         
37946         if(box[0].size == 'md-right'){
37947             pos.push({
37948                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37949                 y : minY + (this.unitWidth + this.gutter) * 1
37950             });
37951             
37952             return pos;
37953         }
37954         
37955         var rand = Math.floor(Math.random() * (4 - box[0].y));
37956         
37957         pos.push({
37958             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37959             y : minY + (this.unitWidth + this.gutter) * rand
37960         });
37961         
37962         return pos;
37963         
37964     },
37965     
37966     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
37967     {
37968         var pos = [];
37969         
37970         if(box[0].size == 'xs'){
37971             
37972             pos.push({
37973                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37974                 y : minY
37975             });
37976
37977             pos.push({
37978                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37979                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
37980             });
37981             
37982             return pos;
37983             
37984         }
37985         
37986         pos.push({
37987             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37988             y : minY
37989         });
37990
37991         pos.push({
37992             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37993             y : minY + (this.unitWidth + this.gutter) * 2
37994         });
37995         
37996         return pos;
37997         
37998     },
37999     
38000     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
38001     {
38002         var pos = [];
38003         
38004         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
38005             
38006             pos.push({
38007                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38008                 y : minY
38009             });
38010
38011             pos.push({
38012                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38013                 y : minY + (this.unitWidth + this.gutter) * 1
38014             });
38015             
38016             pos.push({
38017                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38018                 y : minY + (this.unitWidth + this.gutter) * 2
38019             });
38020             
38021             return pos;
38022             
38023         }
38024         
38025         if(box[0].size == 'xs' && box[1].size == 'xs'){
38026             
38027             pos.push({
38028                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38029                 y : minY
38030             });
38031
38032             pos.push({
38033                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38034                 y : minY
38035             });
38036             
38037             pos.push({
38038                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38039                 y : minY + (this.unitWidth + this.gutter) * 1
38040             });
38041             
38042             return pos;
38043             
38044         }
38045         
38046         pos.push({
38047             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38048             y : minY
38049         });
38050
38051         pos.push({
38052             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38053             y : minY + (this.unitWidth + this.gutter) * 2
38054         });
38055
38056         pos.push({
38057             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38058             y : minY + (this.unitWidth + this.gutter) * 2
38059         });
38060             
38061         return pos;
38062         
38063     },
38064     
38065     getHorizontalFourBoxColPositions : function(maxX, minY, box)
38066     {
38067         var pos = [];
38068         
38069         if(box[0].size == 'xs'){
38070             
38071             pos.push({
38072                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38073                 y : minY
38074             });
38075
38076             pos.push({
38077                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38078                 y : minY
38079             });
38080             
38081             pos.push({
38082                 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),
38083                 y : minY
38084             });
38085             
38086             pos.push({
38087                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
38088                 y : minY + (this.unitWidth + this.gutter) * 1
38089             });
38090             
38091             return pos;
38092             
38093         }
38094         
38095         pos.push({
38096             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38097             y : minY
38098         });
38099         
38100         pos.push({
38101             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38102             y : minY + (this.unitWidth + this.gutter) * 2
38103         });
38104         
38105         pos.push({
38106             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38107             y : minY + (this.unitWidth + this.gutter) * 2
38108         });
38109         
38110         pos.push({
38111             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),
38112             y : minY + (this.unitWidth + this.gutter) * 2
38113         });
38114
38115         return pos;
38116         
38117     },
38118     
38119     /**
38120     * remove a Masonry Brick
38121     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
38122     */
38123     removeBrick : function(brick_id)
38124     {
38125         if (!brick_id) {
38126             return;
38127         }
38128         
38129         for (var i = 0; i<this.bricks.length; i++) {
38130             if (this.bricks[i].id == brick_id) {
38131                 this.bricks.splice(i,1);
38132                 this.el.dom.removeChild(Roo.get(brick_id).dom);
38133                 this.initial();
38134             }
38135         }
38136     },
38137     
38138     /**
38139     * adds a Masonry Brick
38140     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
38141     */
38142     addBrick : function(cfg)
38143     {
38144         var cn = new Roo.bootstrap.MasonryBrick(cfg);
38145         //this.register(cn);
38146         cn.parentId = this.id;
38147         cn.render(this.el);
38148         return cn;
38149     },
38150     
38151     /**
38152     * register a Masonry Brick
38153     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
38154     */
38155     
38156     register : function(brick)
38157     {
38158         this.bricks.push(brick);
38159         brick.masonryId = this.id;
38160     },
38161     
38162     /**
38163     * clear all the Masonry Brick
38164     */
38165     clearAll : function()
38166     {
38167         this.bricks = [];
38168         //this.getChildContainer().dom.innerHTML = "";
38169         this.el.dom.innerHTML = '';
38170     },
38171     
38172     getSelected : function()
38173     {
38174         if (!this.selectedBrick) {
38175             return false;
38176         }
38177         
38178         return this.selectedBrick;
38179     }
38180 });
38181
38182 Roo.apply(Roo.bootstrap.LayoutMasonry, {
38183     
38184     groups: {},
38185      /**
38186     * register a Masonry Layout
38187     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
38188     */
38189     
38190     register : function(layout)
38191     {
38192         this.groups[layout.id] = layout;
38193     },
38194     /**
38195     * fetch a  Masonry Layout based on the masonry layout ID
38196     * @param {string} the masonry layout to add
38197     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
38198     */
38199     
38200     get: function(layout_id) {
38201         if (typeof(this.groups[layout_id]) == 'undefined') {
38202             return false;
38203         }
38204         return this.groups[layout_id] ;
38205     }
38206     
38207     
38208     
38209 });
38210
38211  
38212
38213  /**
38214  *
38215  * This is based on 
38216  * http://masonry.desandro.com
38217  *
38218  * The idea is to render all the bricks based on vertical width...
38219  *
38220  * The original code extends 'outlayer' - we might need to use that....
38221  * 
38222  */
38223
38224
38225 /**
38226  * @class Roo.bootstrap.LayoutMasonryAuto
38227  * @extends Roo.bootstrap.Component
38228  * Bootstrap Layout Masonry class
38229  * 
38230  * @constructor
38231  * Create a new Element
38232  * @param {Object} config The config object
38233  */
38234
38235 Roo.bootstrap.LayoutMasonryAuto = function(config){
38236     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
38237 };
38238
38239 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
38240     
38241       /**
38242      * @cfg {Boolean} isFitWidth  - resize the width..
38243      */   
38244     isFitWidth : false,  // options..
38245     /**
38246      * @cfg {Boolean} isOriginLeft = left align?
38247      */   
38248     isOriginLeft : true,
38249     /**
38250      * @cfg {Boolean} isOriginTop = top align?
38251      */   
38252     isOriginTop : false,
38253     /**
38254      * @cfg {Boolean} isLayoutInstant = no animation?
38255      */   
38256     isLayoutInstant : false, // needed?
38257     /**
38258      * @cfg {Boolean} isResizingContainer = not sure if this is used..
38259      */   
38260     isResizingContainer : true,
38261     /**
38262      * @cfg {Number} columnWidth  width of the columns 
38263      */   
38264     
38265     columnWidth : 0,
38266     
38267     /**
38268      * @cfg {Number} maxCols maximum number of columns
38269      */   
38270     
38271     maxCols: 0,
38272     /**
38273      * @cfg {Number} padHeight padding below box..
38274      */   
38275     
38276     padHeight : 10, 
38277     
38278     /**
38279      * @cfg {Boolean} isAutoInitial defalut true
38280      */   
38281     
38282     isAutoInitial : true, 
38283     
38284     // private?
38285     gutter : 0,
38286     
38287     containerWidth: 0,
38288     initialColumnWidth : 0,
38289     currentSize : null,
38290     
38291     colYs : null, // array.
38292     maxY : 0,
38293     padWidth: 10,
38294     
38295     
38296     tag: 'div',
38297     cls: '',
38298     bricks: null, //CompositeElement
38299     cols : 0, // array?
38300     // element : null, // wrapped now this.el
38301     _isLayoutInited : null, 
38302     
38303     
38304     getAutoCreate : function(){
38305         
38306         var cfg = {
38307             tag: this.tag,
38308             cls: 'blog-masonary-wrapper ' + this.cls,
38309             cn : {
38310                 cls : 'mas-boxes masonary'
38311             }
38312         };
38313         
38314         return cfg;
38315     },
38316     
38317     getChildContainer: function( )
38318     {
38319         if (this.boxesEl) {
38320             return this.boxesEl;
38321         }
38322         
38323         this.boxesEl = this.el.select('.mas-boxes').first();
38324         
38325         return this.boxesEl;
38326     },
38327     
38328     
38329     initEvents : function()
38330     {
38331         var _this = this;
38332         
38333         if(this.isAutoInitial){
38334             Roo.log('hook children rendered');
38335             this.on('childrenrendered', function() {
38336                 Roo.log('children rendered');
38337                 _this.initial();
38338             } ,this);
38339         }
38340         
38341     },
38342     
38343     initial : function()
38344     {
38345         this.reloadItems();
38346
38347         this.currentSize = this.el.getBox(true);
38348
38349         /// was window resize... - let's see if this works..
38350         Roo.EventManager.onWindowResize(this.resize, this); 
38351
38352         if(!this.isAutoInitial){
38353             this.layout();
38354             return;
38355         }
38356         
38357         this.layout.defer(500,this);
38358     },
38359     
38360     reloadItems: function()
38361     {
38362         this.bricks = this.el.select('.masonry-brick', true);
38363         
38364         this.bricks.each(function(b) {
38365             //Roo.log(b.getSize());
38366             if (!b.attr('originalwidth')) {
38367                 b.attr('originalwidth',  b.getSize().width);
38368             }
38369             
38370         });
38371         
38372         Roo.log(this.bricks.elements.length);
38373     },
38374     
38375     resize : function()
38376     {
38377         Roo.log('resize');
38378         var cs = this.el.getBox(true);
38379         
38380         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
38381             Roo.log("no change in with or X");
38382             return;
38383         }
38384         this.currentSize = cs;
38385         this.layout();
38386     },
38387     
38388     layout : function()
38389     {
38390          Roo.log('layout');
38391         this._resetLayout();
38392         //this._manageStamps();
38393       
38394         // don't animate first layout
38395         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38396         this.layoutItems( isInstant );
38397       
38398         // flag for initalized
38399         this._isLayoutInited = true;
38400     },
38401     
38402     layoutItems : function( isInstant )
38403     {
38404         //var items = this._getItemsForLayout( this.items );
38405         // original code supports filtering layout items.. we just ignore it..
38406         
38407         this._layoutItems( this.bricks , isInstant );
38408       
38409         this._postLayout();
38410     },
38411     _layoutItems : function ( items , isInstant)
38412     {
38413        //this.fireEvent( 'layout', this, items );
38414     
38415
38416         if ( !items || !items.elements.length ) {
38417           // no items, emit event with empty array
38418             return;
38419         }
38420
38421         var queue = [];
38422         items.each(function(item) {
38423             Roo.log("layout item");
38424             Roo.log(item);
38425             // get x/y object from method
38426             var position = this._getItemLayoutPosition( item );
38427             // enqueue
38428             position.item = item;
38429             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
38430             queue.push( position );
38431         }, this);
38432       
38433         this._processLayoutQueue( queue );
38434     },
38435     /** Sets position of item in DOM
38436     * @param {Element} item
38437     * @param {Number} x - horizontal position
38438     * @param {Number} y - vertical position
38439     * @param {Boolean} isInstant - disables transitions
38440     */
38441     _processLayoutQueue : function( queue )
38442     {
38443         for ( var i=0, len = queue.length; i < len; i++ ) {
38444             var obj = queue[i];
38445             obj.item.position('absolute');
38446             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
38447         }
38448     },
38449       
38450     
38451     /**
38452     * Any logic you want to do after each layout,
38453     * i.e. size the container
38454     */
38455     _postLayout : function()
38456     {
38457         this.resizeContainer();
38458     },
38459     
38460     resizeContainer : function()
38461     {
38462         if ( !this.isResizingContainer ) {
38463             return;
38464         }
38465         var size = this._getContainerSize();
38466         if ( size ) {
38467             this.el.setSize(size.width,size.height);
38468             this.boxesEl.setSize(size.width,size.height);
38469         }
38470     },
38471     
38472     
38473     
38474     _resetLayout : function()
38475     {
38476         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
38477         this.colWidth = this.el.getWidth();
38478         //this.gutter = this.el.getWidth(); 
38479         
38480         this.measureColumns();
38481
38482         // reset column Y
38483         var i = this.cols;
38484         this.colYs = [];
38485         while (i--) {
38486             this.colYs.push( 0 );
38487         }
38488     
38489         this.maxY = 0;
38490     },
38491
38492     measureColumns : function()
38493     {
38494         this.getContainerWidth();
38495       // if columnWidth is 0, default to outerWidth of first item
38496         if ( !this.columnWidth ) {
38497             var firstItem = this.bricks.first();
38498             Roo.log(firstItem);
38499             this.columnWidth  = this.containerWidth;
38500             if (firstItem && firstItem.attr('originalwidth') ) {
38501                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
38502             }
38503             // columnWidth fall back to item of first element
38504             Roo.log("set column width?");
38505                         this.initialColumnWidth = this.columnWidth  ;
38506
38507             // if first elem has no width, default to size of container
38508             
38509         }
38510         
38511         
38512         if (this.initialColumnWidth) {
38513             this.columnWidth = this.initialColumnWidth;
38514         }
38515         
38516         
38517             
38518         // column width is fixed at the top - however if container width get's smaller we should
38519         // reduce it...
38520         
38521         // this bit calcs how man columns..
38522             
38523         var columnWidth = this.columnWidth += this.gutter;
38524       
38525         // calculate columns
38526         var containerWidth = this.containerWidth + this.gutter;
38527         
38528         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
38529         // fix rounding errors, typically with gutters
38530         var excess = columnWidth - containerWidth % columnWidth;
38531         
38532         
38533         // if overshoot is less than a pixel, round up, otherwise floor it
38534         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
38535         cols = Math[ mathMethod ]( cols );
38536         this.cols = Math.max( cols, 1 );
38537         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38538         
38539          // padding positioning..
38540         var totalColWidth = this.cols * this.columnWidth;
38541         var padavail = this.containerWidth - totalColWidth;
38542         // so for 2 columns - we need 3 'pads'
38543         
38544         var padNeeded = (1+this.cols) * this.padWidth;
38545         
38546         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
38547         
38548         this.columnWidth += padExtra
38549         //this.padWidth = Math.floor(padavail /  ( this.cols));
38550         
38551         // adjust colum width so that padding is fixed??
38552         
38553         // we have 3 columns ... total = width * 3
38554         // we have X left over... that should be used by 
38555         
38556         //if (this.expandC) {
38557             
38558         //}
38559         
38560         
38561         
38562     },
38563     
38564     getContainerWidth : function()
38565     {
38566        /* // container is parent if fit width
38567         var container = this.isFitWidth ? this.element.parentNode : this.element;
38568         // check that this.size and size are there
38569         // IE8 triggers resize on body size change, so they might not be
38570         
38571         var size = getSize( container );  //FIXME
38572         this.containerWidth = size && size.innerWidth; //FIXME
38573         */
38574          
38575         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38576         
38577     },
38578     
38579     _getItemLayoutPosition : function( item )  // what is item?
38580     {
38581         // we resize the item to our columnWidth..
38582       
38583         item.setWidth(this.columnWidth);
38584         item.autoBoxAdjust  = false;
38585         
38586         var sz = item.getSize();
38587  
38588         // how many columns does this brick span
38589         var remainder = this.containerWidth % this.columnWidth;
38590         
38591         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
38592         // round if off by 1 pixel, otherwise use ceil
38593         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
38594         colSpan = Math.min( colSpan, this.cols );
38595         
38596         // normally this should be '1' as we dont' currently allow multi width columns..
38597         
38598         var colGroup = this._getColGroup( colSpan );
38599         // get the minimum Y value from the columns
38600         var minimumY = Math.min.apply( Math, colGroup );
38601         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38602         
38603         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
38604          
38605         // position the brick
38606         var position = {
38607             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
38608             y: this.currentSize.y + minimumY + this.padHeight
38609         };
38610         
38611         Roo.log(position);
38612         // apply setHeight to necessary columns
38613         var setHeight = minimumY + sz.height + this.padHeight;
38614         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38615         
38616         var setSpan = this.cols + 1 - colGroup.length;
38617         for ( var i = 0; i < setSpan; i++ ) {
38618           this.colYs[ shortColIndex + i ] = setHeight ;
38619         }
38620       
38621         return position;
38622     },
38623     
38624     /**
38625      * @param {Number} colSpan - number of columns the element spans
38626      * @returns {Array} colGroup
38627      */
38628     _getColGroup : function( colSpan )
38629     {
38630         if ( colSpan < 2 ) {
38631           // if brick spans only one column, use all the column Ys
38632           return this.colYs;
38633         }
38634       
38635         var colGroup = [];
38636         // how many different places could this brick fit horizontally
38637         var groupCount = this.cols + 1 - colSpan;
38638         // for each group potential horizontal position
38639         for ( var i = 0; i < groupCount; i++ ) {
38640           // make an array of colY values for that one group
38641           var groupColYs = this.colYs.slice( i, i + colSpan );
38642           // and get the max value of the array
38643           colGroup[i] = Math.max.apply( Math, groupColYs );
38644         }
38645         return colGroup;
38646     },
38647     /*
38648     _manageStamp : function( stamp )
38649     {
38650         var stampSize =  stamp.getSize();
38651         var offset = stamp.getBox();
38652         // get the columns that this stamp affects
38653         var firstX = this.isOriginLeft ? offset.x : offset.right;
38654         var lastX = firstX + stampSize.width;
38655         var firstCol = Math.floor( firstX / this.columnWidth );
38656         firstCol = Math.max( 0, firstCol );
38657         
38658         var lastCol = Math.floor( lastX / this.columnWidth );
38659         // lastCol should not go over if multiple of columnWidth #425
38660         lastCol -= lastX % this.columnWidth ? 0 : 1;
38661         lastCol = Math.min( this.cols - 1, lastCol );
38662         
38663         // set colYs to bottom of the stamp
38664         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
38665             stampSize.height;
38666             
38667         for ( var i = firstCol; i <= lastCol; i++ ) {
38668           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
38669         }
38670     },
38671     */
38672     
38673     _getContainerSize : function()
38674     {
38675         this.maxY = Math.max.apply( Math, this.colYs );
38676         var size = {
38677             height: this.maxY
38678         };
38679       
38680         if ( this.isFitWidth ) {
38681             size.width = this._getContainerFitWidth();
38682         }
38683       
38684         return size;
38685     },
38686     
38687     _getContainerFitWidth : function()
38688     {
38689         var unusedCols = 0;
38690         // count unused columns
38691         var i = this.cols;
38692         while ( --i ) {
38693           if ( this.colYs[i] !== 0 ) {
38694             break;
38695           }
38696           unusedCols++;
38697         }
38698         // fit container to columns that have been used
38699         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
38700     },
38701     
38702     needsResizeLayout : function()
38703     {
38704         var previousWidth = this.containerWidth;
38705         this.getContainerWidth();
38706         return previousWidth !== this.containerWidth;
38707     }
38708  
38709 });
38710
38711  
38712
38713  /*
38714  * - LGPL
38715  *
38716  * element
38717  * 
38718  */
38719
38720 /**
38721  * @class Roo.bootstrap.MasonryBrick
38722  * @extends Roo.bootstrap.Component
38723  * Bootstrap MasonryBrick class
38724  * 
38725  * @constructor
38726  * Create a new MasonryBrick
38727  * @param {Object} config The config object
38728  */
38729
38730 Roo.bootstrap.MasonryBrick = function(config){
38731     
38732     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
38733     
38734     Roo.bootstrap.MasonryBrick.register(this);
38735     
38736     this.addEvents({
38737         // raw events
38738         /**
38739          * @event click
38740          * When a MasonryBrick is clcik
38741          * @param {Roo.bootstrap.MasonryBrick} this
38742          * @param {Roo.EventObject} e
38743          */
38744         "click" : true
38745     });
38746 };
38747
38748 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
38749     
38750     /**
38751      * @cfg {String} title
38752      */   
38753     title : '',
38754     /**
38755      * @cfg {String} html
38756      */   
38757     html : '',
38758     /**
38759      * @cfg {String} bgimage
38760      */   
38761     bgimage : '',
38762     /**
38763      * @cfg {String} videourl
38764      */   
38765     videourl : '',
38766     /**
38767      * @cfg {String} cls
38768      */   
38769     cls : '',
38770     /**
38771      * @cfg {String} href
38772      */   
38773     href : '',
38774     /**
38775      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
38776      */   
38777     size : 'xs',
38778     
38779     /**
38780      * @cfg {String} placetitle (center|bottom)
38781      */   
38782     placetitle : '',
38783     
38784     /**
38785      * @cfg {Boolean} isFitContainer defalut true
38786      */   
38787     isFitContainer : true, 
38788     
38789     /**
38790      * @cfg {Boolean} preventDefault defalut false
38791      */   
38792     preventDefault : false, 
38793     
38794     /**
38795      * @cfg {Boolean} inverse defalut false
38796      */   
38797     maskInverse : false, 
38798     
38799     getAutoCreate : function()
38800     {
38801         if(!this.isFitContainer){
38802             return this.getSplitAutoCreate();
38803         }
38804         
38805         var cls = 'masonry-brick masonry-brick-full';
38806         
38807         if(this.href.length){
38808             cls += ' masonry-brick-link';
38809         }
38810         
38811         if(this.bgimage.length){
38812             cls += ' masonry-brick-image';
38813         }
38814         
38815         if(this.maskInverse){
38816             cls += ' mask-inverse';
38817         }
38818         
38819         if(!this.html.length && !this.maskInverse && !this.videourl.length){
38820             cls += ' enable-mask';
38821         }
38822         
38823         if(this.size){
38824             cls += ' masonry-' + this.size + '-brick';
38825         }
38826         
38827         if(this.placetitle.length){
38828             
38829             switch (this.placetitle) {
38830                 case 'center' :
38831                     cls += ' masonry-center-title';
38832                     break;
38833                 case 'bottom' :
38834                     cls += ' masonry-bottom-title';
38835                     break;
38836                 default:
38837                     break;
38838             }
38839             
38840         } else {
38841             if(!this.html.length && !this.bgimage.length){
38842                 cls += ' masonry-center-title';
38843             }
38844
38845             if(!this.html.length && this.bgimage.length){
38846                 cls += ' masonry-bottom-title';
38847             }
38848         }
38849         
38850         if(this.cls){
38851             cls += ' ' + this.cls;
38852         }
38853         
38854         var cfg = {
38855             tag: (this.href.length) ? 'a' : 'div',
38856             cls: cls,
38857             cn: [
38858                 {
38859                     tag: 'div',
38860                     cls: 'masonry-brick-mask'
38861                 },
38862                 {
38863                     tag: 'div',
38864                     cls: 'masonry-brick-paragraph',
38865                     cn: []
38866                 }
38867             ]
38868         };
38869         
38870         if(this.href.length){
38871             cfg.href = this.href;
38872         }
38873         
38874         var cn = cfg.cn[1].cn;
38875         
38876         if(this.title.length){
38877             cn.push({
38878                 tag: 'h4',
38879                 cls: 'masonry-brick-title',
38880                 html: this.title
38881             });
38882         }
38883         
38884         if(this.html.length){
38885             cn.push({
38886                 tag: 'p',
38887                 cls: 'masonry-brick-text',
38888                 html: this.html
38889             });
38890         }
38891         
38892         if (!this.title.length && !this.html.length) {
38893             cfg.cn[1].cls += ' hide';
38894         }
38895         
38896         if(this.bgimage.length){
38897             cfg.cn.push({
38898                 tag: 'img',
38899                 cls: 'masonry-brick-image-view',
38900                 src: this.bgimage
38901             });
38902         }
38903         
38904         if(this.videourl.length){
38905             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38906             // youtube support only?
38907             cfg.cn.push({
38908                 tag: 'iframe',
38909                 cls: 'masonry-brick-image-view',
38910                 src: vurl,
38911                 frameborder : 0,
38912                 allowfullscreen : true
38913             });
38914         }
38915         
38916         return cfg;
38917         
38918     },
38919     
38920     getSplitAutoCreate : function()
38921     {
38922         var cls = 'masonry-brick masonry-brick-split';
38923         
38924         if(this.href.length){
38925             cls += ' masonry-brick-link';
38926         }
38927         
38928         if(this.bgimage.length){
38929             cls += ' masonry-brick-image';
38930         }
38931         
38932         if(this.size){
38933             cls += ' masonry-' + this.size + '-brick';
38934         }
38935         
38936         switch (this.placetitle) {
38937             case 'center' :
38938                 cls += ' masonry-center-title';
38939                 break;
38940             case 'bottom' :
38941                 cls += ' masonry-bottom-title';
38942                 break;
38943             default:
38944                 if(!this.bgimage.length){
38945                     cls += ' masonry-center-title';
38946                 }
38947
38948                 if(this.bgimage.length){
38949                     cls += ' masonry-bottom-title';
38950                 }
38951                 break;
38952         }
38953         
38954         if(this.cls){
38955             cls += ' ' + this.cls;
38956         }
38957         
38958         var cfg = {
38959             tag: (this.href.length) ? 'a' : 'div',
38960             cls: cls,
38961             cn: [
38962                 {
38963                     tag: 'div',
38964                     cls: 'masonry-brick-split-head',
38965                     cn: [
38966                         {
38967                             tag: 'div',
38968                             cls: 'masonry-brick-paragraph',
38969                             cn: []
38970                         }
38971                     ]
38972                 },
38973                 {
38974                     tag: 'div',
38975                     cls: 'masonry-brick-split-body',
38976                     cn: []
38977                 }
38978             ]
38979         };
38980         
38981         if(this.href.length){
38982             cfg.href = this.href;
38983         }
38984         
38985         if(this.title.length){
38986             cfg.cn[0].cn[0].cn.push({
38987                 tag: 'h4',
38988                 cls: 'masonry-brick-title',
38989                 html: this.title
38990             });
38991         }
38992         
38993         if(this.html.length){
38994             cfg.cn[1].cn.push({
38995                 tag: 'p',
38996                 cls: 'masonry-brick-text',
38997                 html: this.html
38998             });
38999         }
39000
39001         if(this.bgimage.length){
39002             cfg.cn[0].cn.push({
39003                 tag: 'img',
39004                 cls: 'masonry-brick-image-view',
39005                 src: this.bgimage
39006             });
39007         }
39008         
39009         if(this.videourl.length){
39010             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
39011             // youtube support only?
39012             cfg.cn[0].cn.cn.push({
39013                 tag: 'iframe',
39014                 cls: 'masonry-brick-image-view',
39015                 src: vurl,
39016                 frameborder : 0,
39017                 allowfullscreen : true
39018             });
39019         }
39020         
39021         return cfg;
39022     },
39023     
39024     initEvents: function() 
39025     {
39026         switch (this.size) {
39027             case 'xs' :
39028                 this.x = 1;
39029                 this.y = 1;
39030                 break;
39031             case 'sm' :
39032                 this.x = 2;
39033                 this.y = 2;
39034                 break;
39035             case 'md' :
39036             case 'md-left' :
39037             case 'md-right' :
39038                 this.x = 3;
39039                 this.y = 3;
39040                 break;
39041             case 'tall' :
39042                 this.x = 2;
39043                 this.y = 3;
39044                 break;
39045             case 'wide' :
39046                 this.x = 3;
39047                 this.y = 2;
39048                 break;
39049             case 'wide-thin' :
39050                 this.x = 3;
39051                 this.y = 1;
39052                 break;
39053                         
39054             default :
39055                 break;
39056         }
39057         
39058         if(Roo.isTouch){
39059             this.el.on('touchstart', this.onTouchStart, this);
39060             this.el.on('touchmove', this.onTouchMove, this);
39061             this.el.on('touchend', this.onTouchEnd, this);
39062             this.el.on('contextmenu', this.onContextMenu, this);
39063         } else {
39064             this.el.on('mouseenter'  ,this.enter, this);
39065             this.el.on('mouseleave', this.leave, this);
39066             this.el.on('click', this.onClick, this);
39067         }
39068         
39069         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
39070             this.parent().bricks.push(this);   
39071         }
39072         
39073     },
39074     
39075     onClick: function(e, el)
39076     {
39077         var time = this.endTimer - this.startTimer;
39078         // Roo.log(e.preventDefault());
39079         if(Roo.isTouch){
39080             if(time > 1000){
39081                 e.preventDefault();
39082                 return;
39083             }
39084         }
39085         
39086         if(!this.preventDefault){
39087             return;
39088         }
39089         
39090         e.preventDefault();
39091         
39092         if (this.activeClass != '') {
39093             this.selectBrick();
39094         }
39095         
39096         this.fireEvent('click', this, e);
39097     },
39098     
39099     enter: function(e, el)
39100     {
39101         e.preventDefault();
39102         
39103         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
39104             return;
39105         }
39106         
39107         if(this.bgimage.length && this.html.length){
39108             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
39109         }
39110     },
39111     
39112     leave: function(e, el)
39113     {
39114         e.preventDefault();
39115         
39116         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
39117             return;
39118         }
39119         
39120         if(this.bgimage.length && this.html.length){
39121             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
39122         }
39123     },
39124     
39125     onTouchStart: function(e, el)
39126     {
39127 //        e.preventDefault();
39128         
39129         this.touchmoved = false;
39130         
39131         if(!this.isFitContainer){
39132             return;
39133         }
39134         
39135         if(!this.bgimage.length || !this.html.length){
39136             return;
39137         }
39138         
39139         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
39140         
39141         this.timer = new Date().getTime();
39142         
39143     },
39144     
39145     onTouchMove: function(e, el)
39146     {
39147         this.touchmoved = true;
39148     },
39149     
39150     onContextMenu : function(e,el)
39151     {
39152         e.preventDefault();
39153         e.stopPropagation();
39154         return false;
39155     },
39156     
39157     onTouchEnd: function(e, el)
39158     {
39159 //        e.preventDefault();
39160         
39161         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
39162         
39163             this.leave(e,el);
39164             
39165             return;
39166         }
39167         
39168         if(!this.bgimage.length || !this.html.length){
39169             
39170             if(this.href.length){
39171                 window.location.href = this.href;
39172             }
39173             
39174             return;
39175         }
39176         
39177         if(!this.isFitContainer){
39178             return;
39179         }
39180         
39181         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
39182         
39183         window.location.href = this.href;
39184     },
39185     
39186     //selection on single brick only
39187     selectBrick : function() {
39188         
39189         if (!this.parentId) {
39190             return;
39191         }
39192         
39193         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
39194         var index = m.selectedBrick.indexOf(this.id);
39195         
39196         if ( index > -1) {
39197             m.selectedBrick.splice(index,1);
39198             this.el.removeClass(this.activeClass);
39199             return;
39200         }
39201         
39202         for(var i = 0; i < m.selectedBrick.length; i++) {
39203             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
39204             b.el.removeClass(b.activeClass);
39205         }
39206         
39207         m.selectedBrick = [];
39208         
39209         m.selectedBrick.push(this.id);
39210         this.el.addClass(this.activeClass);
39211         return;
39212     },
39213     
39214     isSelected : function(){
39215         return this.el.hasClass(this.activeClass);
39216         
39217     }
39218 });
39219
39220 Roo.apply(Roo.bootstrap.MasonryBrick, {
39221     
39222     //groups: {},
39223     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
39224      /**
39225     * register a Masonry Brick
39226     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39227     */
39228     
39229     register : function(brick)
39230     {
39231         //this.groups[brick.id] = brick;
39232         this.groups.add(brick.id, brick);
39233     },
39234     /**
39235     * fetch a  masonry brick based on the masonry brick ID
39236     * @param {string} the masonry brick to add
39237     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
39238     */
39239     
39240     get: function(brick_id) 
39241     {
39242         // if (typeof(this.groups[brick_id]) == 'undefined') {
39243         //     return false;
39244         // }
39245         // return this.groups[brick_id] ;
39246         
39247         if(this.groups.key(brick_id)) {
39248             return this.groups.key(brick_id);
39249         }
39250         
39251         return false;
39252     }
39253     
39254     
39255     
39256 });
39257
39258  /*
39259  * - LGPL
39260  *
39261  * element
39262  * 
39263  */
39264
39265 /**
39266  * @class Roo.bootstrap.Brick
39267  * @extends Roo.bootstrap.Component
39268  * Bootstrap Brick class
39269  * 
39270  * @constructor
39271  * Create a new Brick
39272  * @param {Object} config The config object
39273  */
39274
39275 Roo.bootstrap.Brick = function(config){
39276     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
39277     
39278     this.addEvents({
39279         // raw events
39280         /**
39281          * @event click
39282          * When a Brick is click
39283          * @param {Roo.bootstrap.Brick} this
39284          * @param {Roo.EventObject} e
39285          */
39286         "click" : true
39287     });
39288 };
39289
39290 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
39291     
39292     /**
39293      * @cfg {String} title
39294      */   
39295     title : '',
39296     /**
39297      * @cfg {String} html
39298      */   
39299     html : '',
39300     /**
39301      * @cfg {String} bgimage
39302      */   
39303     bgimage : '',
39304     /**
39305      * @cfg {String} cls
39306      */   
39307     cls : '',
39308     /**
39309      * @cfg {String} href
39310      */   
39311     href : '',
39312     /**
39313      * @cfg {String} video
39314      */   
39315     video : '',
39316     /**
39317      * @cfg {Boolean} square
39318      */   
39319     square : true,
39320     
39321     getAutoCreate : function()
39322     {
39323         var cls = 'roo-brick';
39324         
39325         if(this.href.length){
39326             cls += ' roo-brick-link';
39327         }
39328         
39329         if(this.bgimage.length){
39330             cls += ' roo-brick-image';
39331         }
39332         
39333         if(!this.html.length && !this.bgimage.length){
39334             cls += ' roo-brick-center-title';
39335         }
39336         
39337         if(!this.html.length && this.bgimage.length){
39338             cls += ' roo-brick-bottom-title';
39339         }
39340         
39341         if(this.cls){
39342             cls += ' ' + this.cls;
39343         }
39344         
39345         var cfg = {
39346             tag: (this.href.length) ? 'a' : 'div',
39347             cls: cls,
39348             cn: [
39349                 {
39350                     tag: 'div',
39351                     cls: 'roo-brick-paragraph',
39352                     cn: []
39353                 }
39354             ]
39355         };
39356         
39357         if(this.href.length){
39358             cfg.href = this.href;
39359         }
39360         
39361         var cn = cfg.cn[0].cn;
39362         
39363         if(this.title.length){
39364             cn.push({
39365                 tag: 'h4',
39366                 cls: 'roo-brick-title',
39367                 html: this.title
39368             });
39369         }
39370         
39371         if(this.html.length){
39372             cn.push({
39373                 tag: 'p',
39374                 cls: 'roo-brick-text',
39375                 html: this.html
39376             });
39377         } else {
39378             cn.cls += ' hide';
39379         }
39380         
39381         if(this.bgimage.length){
39382             cfg.cn.push({
39383                 tag: 'img',
39384                 cls: 'roo-brick-image-view',
39385                 src: this.bgimage
39386             });
39387         }
39388         
39389         return cfg;
39390     },
39391     
39392     initEvents: function() 
39393     {
39394         if(this.title.length || this.html.length){
39395             this.el.on('mouseenter'  ,this.enter, this);
39396             this.el.on('mouseleave', this.leave, this);
39397         }
39398         
39399         Roo.EventManager.onWindowResize(this.resize, this); 
39400         
39401         if(this.bgimage.length){
39402             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
39403             this.imageEl.on('load', this.onImageLoad, this);
39404             return;
39405         }
39406         
39407         this.resize();
39408     },
39409     
39410     onImageLoad : function()
39411     {
39412         this.resize();
39413     },
39414     
39415     resize : function()
39416     {
39417         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
39418         
39419         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
39420         
39421         if(this.bgimage.length){
39422             var image = this.el.select('.roo-brick-image-view', true).first();
39423             
39424             image.setWidth(paragraph.getWidth());
39425             
39426             if(this.square){
39427                 image.setHeight(paragraph.getWidth());
39428             }
39429             
39430             this.el.setHeight(image.getHeight());
39431             paragraph.setHeight(image.getHeight());
39432             
39433         }
39434         
39435     },
39436     
39437     enter: function(e, el)
39438     {
39439         e.preventDefault();
39440         
39441         if(this.bgimage.length){
39442             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
39443             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
39444         }
39445     },
39446     
39447     leave: function(e, el)
39448     {
39449         e.preventDefault();
39450         
39451         if(this.bgimage.length){
39452             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
39453             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
39454         }
39455     }
39456     
39457 });
39458
39459  
39460
39461  /*
39462  * - LGPL
39463  *
39464  * Number field 
39465  */
39466
39467 /**
39468  * @class Roo.bootstrap.form.NumberField
39469  * @extends Roo.bootstrap.form.Input
39470  * Bootstrap NumberField class
39471  * 
39472  * 
39473  * 
39474  * 
39475  * @constructor
39476  * Create a new NumberField
39477  * @param {Object} config The config object
39478  */
39479
39480 Roo.bootstrap.form.NumberField = function(config){
39481     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
39482 };
39483
39484 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
39485     
39486     /**
39487      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39488      */
39489     allowDecimals : true,
39490     /**
39491      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39492      */
39493     decimalSeparator : ".",
39494     /**
39495      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39496      */
39497     decimalPrecision : 2,
39498     /**
39499      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39500      */
39501     allowNegative : true,
39502     
39503     /**
39504      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
39505      */
39506     allowZero: true,
39507     /**
39508      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39509      */
39510     minValue : Number.NEGATIVE_INFINITY,
39511     /**
39512      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39513      */
39514     maxValue : Number.MAX_VALUE,
39515     /**
39516      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39517      */
39518     minText : "The minimum value for this field is {0}",
39519     /**
39520      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39521      */
39522     maxText : "The maximum value for this field is {0}",
39523     /**
39524      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39525      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39526      */
39527     nanText : "{0} is not a valid number",
39528     /**
39529      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
39530      */
39531     thousandsDelimiter : false,
39532     /**
39533      * @cfg {String} valueAlign alignment of value
39534      */
39535     valueAlign : "left",
39536
39537     getAutoCreate : function()
39538     {
39539         var hiddenInput = {
39540             tag: 'input',
39541             type: 'hidden',
39542             id: Roo.id(),
39543             cls: 'hidden-number-input'
39544         };
39545         
39546         if (this.name) {
39547             hiddenInput.name = this.name;
39548         }
39549         
39550         this.name = '';
39551         
39552         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
39553         
39554         this.name = hiddenInput.name;
39555         
39556         if(cfg.cn.length > 0) {
39557             cfg.cn.push(hiddenInput);
39558         }
39559         
39560         return cfg;
39561     },
39562
39563     // private
39564     initEvents : function()
39565     {   
39566         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
39567         
39568         var allowed = "0123456789";
39569         
39570         if(this.allowDecimals){
39571             allowed += this.decimalSeparator;
39572         }
39573         
39574         if(this.allowNegative){
39575             allowed += "-";
39576         }
39577         
39578         if(this.thousandsDelimiter) {
39579             allowed += ",";
39580         }
39581         
39582         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39583         
39584         var keyPress = function(e){
39585             
39586             var k = e.getKey();
39587             
39588             var c = e.getCharCode();
39589             
39590             if(
39591                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39592                     allowed.indexOf(String.fromCharCode(c)) === -1
39593             ){
39594                 e.stopEvent();
39595                 return;
39596             }
39597             
39598             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39599                 return;
39600             }
39601             
39602             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39603                 e.stopEvent();
39604             }
39605         };
39606         
39607         this.el.on("keypress", keyPress, this);
39608     },
39609     
39610     validateValue : function(value)
39611     {
39612         
39613         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
39614             return false;
39615         }
39616         
39617         var num = this.parseValue(value);
39618         
39619         if(isNaN(num)){
39620             this.markInvalid(String.format(this.nanText, value));
39621             return false;
39622         }
39623         
39624         if(num < this.minValue){
39625             this.markInvalid(String.format(this.minText, this.minValue));
39626             return false;
39627         }
39628         
39629         if(num > this.maxValue){
39630             this.markInvalid(String.format(this.maxText, this.maxValue));
39631             return false;
39632         }
39633         
39634         return true;
39635     },
39636
39637     getValue : function()
39638     {
39639         var v = this.hiddenEl().getValue();
39640         
39641         return this.fixPrecision(this.parseValue(v));
39642     },
39643
39644     parseValue : function(value)
39645     {
39646         if(this.thousandsDelimiter) {
39647             value += "";
39648             r = new RegExp(",", "g");
39649             value = value.replace(r, "");
39650         }
39651         
39652         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39653         return isNaN(value) ? '' : value;
39654     },
39655
39656     fixPrecision : function(value)
39657     {
39658         if(this.thousandsDelimiter) {
39659             value += "";
39660             r = new RegExp(",", "g");
39661             value = value.replace(r, "");
39662         }
39663         
39664         var nan = isNaN(value);
39665         
39666         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
39667             return nan ? '' : value;
39668         }
39669         return parseFloat(value).toFixed(this.decimalPrecision);
39670     },
39671
39672     setValue : function(v)
39673     {
39674         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
39675         
39676         this.value = v;
39677         
39678         if(this.rendered){
39679             
39680             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39681             
39682             this.inputEl().dom.value = (v == '') ? '' :
39683                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
39684             
39685             if(!this.allowZero && v === '0') {
39686                 this.hiddenEl().dom.value = '';
39687                 this.inputEl().dom.value = '';
39688             }
39689             
39690             this.validate();
39691         }
39692     },
39693
39694     decimalPrecisionFcn : function(v)
39695     {
39696         return Math.floor(v);
39697     },
39698
39699     beforeBlur : function()
39700     {
39701         var v = this.parseValue(this.getRawValue());
39702         
39703         if(v || v === 0 || v === ''){
39704             this.setValue(v);
39705         }
39706     },
39707     
39708     hiddenEl : function()
39709     {
39710         return this.el.select('input.hidden-number-input',true).first();
39711     }
39712     
39713 });
39714
39715  
39716
39717 /*
39718 * Licence: LGPL
39719 */
39720
39721 /**
39722  * @class Roo.bootstrap.DocumentSlider
39723  * @extends Roo.bootstrap.Component
39724  * Bootstrap DocumentSlider class
39725  * 
39726  * @constructor
39727  * Create a new DocumentViewer
39728  * @param {Object} config The config object
39729  */
39730
39731 Roo.bootstrap.DocumentSlider = function(config){
39732     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
39733     
39734     this.files = [];
39735     
39736     this.addEvents({
39737         /**
39738          * @event initial
39739          * Fire after initEvent
39740          * @param {Roo.bootstrap.DocumentSlider} this
39741          */
39742         "initial" : true,
39743         /**
39744          * @event update
39745          * Fire after update
39746          * @param {Roo.bootstrap.DocumentSlider} this
39747          */
39748         "update" : true,
39749         /**
39750          * @event click
39751          * Fire after click
39752          * @param {Roo.bootstrap.DocumentSlider} this
39753          */
39754         "click" : true
39755     });
39756 };
39757
39758 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
39759     
39760     files : false,
39761     
39762     indicator : 0,
39763     
39764     getAutoCreate : function()
39765     {
39766         var cfg = {
39767             tag : 'div',
39768             cls : 'roo-document-slider',
39769             cn : [
39770                 {
39771                     tag : 'div',
39772                     cls : 'roo-document-slider-header',
39773                     cn : [
39774                         {
39775                             tag : 'div',
39776                             cls : 'roo-document-slider-header-title'
39777                         }
39778                     ]
39779                 },
39780                 {
39781                     tag : 'div',
39782                     cls : 'roo-document-slider-body',
39783                     cn : [
39784                         {
39785                             tag : 'div',
39786                             cls : 'roo-document-slider-prev',
39787                             cn : [
39788                                 {
39789                                     tag : 'i',
39790                                     cls : 'fa fa-chevron-left'
39791                                 }
39792                             ]
39793                         },
39794                         {
39795                             tag : 'div',
39796                             cls : 'roo-document-slider-thumb',
39797                             cn : [
39798                                 {
39799                                     tag : 'img',
39800                                     cls : 'roo-document-slider-image'
39801                                 }
39802                             ]
39803                         },
39804                         {
39805                             tag : 'div',
39806                             cls : 'roo-document-slider-next',
39807                             cn : [
39808                                 {
39809                                     tag : 'i',
39810                                     cls : 'fa fa-chevron-right'
39811                                 }
39812                             ]
39813                         }
39814                     ]
39815                 }
39816             ]
39817         };
39818         
39819         return cfg;
39820     },
39821     
39822     initEvents : function()
39823     {
39824         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
39825         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
39826         
39827         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
39828         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
39829         
39830         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
39831         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
39832         
39833         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
39834         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
39835         
39836         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
39837         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
39838         
39839         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
39840         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39841         
39842         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
39843         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39844         
39845         this.thumbEl.on('click', this.onClick, this);
39846         
39847         this.prevIndicator.on('click', this.prev, this);
39848         
39849         this.nextIndicator.on('click', this.next, this);
39850         
39851     },
39852     
39853     initial : function()
39854     {
39855         if(this.files.length){
39856             this.indicator = 1;
39857             this.update()
39858         }
39859         
39860         this.fireEvent('initial', this);
39861     },
39862     
39863     update : function()
39864     {
39865         this.imageEl.attr('src', this.files[this.indicator - 1]);
39866         
39867         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
39868         
39869         this.prevIndicator.show();
39870         
39871         if(this.indicator == 1){
39872             this.prevIndicator.hide();
39873         }
39874         
39875         this.nextIndicator.show();
39876         
39877         if(this.indicator == this.files.length){
39878             this.nextIndicator.hide();
39879         }
39880         
39881         this.thumbEl.scrollTo('top');
39882         
39883         this.fireEvent('update', this);
39884     },
39885     
39886     onClick : function(e)
39887     {
39888         e.preventDefault();
39889         
39890         this.fireEvent('click', this);
39891     },
39892     
39893     prev : function(e)
39894     {
39895         e.preventDefault();
39896         
39897         this.indicator = Math.max(1, this.indicator - 1);
39898         
39899         this.update();
39900     },
39901     
39902     next : function(e)
39903     {
39904         e.preventDefault();
39905         
39906         this.indicator = Math.min(this.files.length, this.indicator + 1);
39907         
39908         this.update();
39909     }
39910 });
39911 /*
39912  * - LGPL
39913  *
39914  * RadioSet
39915  *
39916  *
39917  */
39918
39919 /**
39920  * @class Roo.bootstrap.form.RadioSet
39921  * @extends Roo.bootstrap.form.Input
39922  * @children Roo.bootstrap.form.Radio
39923  * Bootstrap RadioSet class
39924  * @cfg {String} indicatorpos (left|right) default left
39925  * @cfg {Boolean} inline (true|false) inline the element (default true)
39926  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
39927  * @constructor
39928  * Create a new RadioSet
39929  * @param {Object} config The config object
39930  */
39931
39932 Roo.bootstrap.form.RadioSet = function(config){
39933     
39934     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
39935     
39936     this.radioes = [];
39937     
39938     Roo.bootstrap.form.RadioSet.register(this);
39939     
39940     this.addEvents({
39941         /**
39942         * @event check
39943         * Fires when the element is checked or unchecked.
39944         * @param {Roo.bootstrap.form.RadioSet} this This radio
39945         * @param {Roo.bootstrap.form.Radio} item The checked item
39946         */
39947        check : true,
39948        /**
39949         * @event click
39950         * Fires when the element is click.
39951         * @param {Roo.bootstrap.form.RadioSet} this This radio set
39952         * @param {Roo.bootstrap.form.Radio} item The checked item
39953         * @param {Roo.EventObject} e The event object
39954         */
39955        click : true
39956     });
39957     
39958 };
39959
39960 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
39961
39962     radioes : false,
39963     
39964     inline : true,
39965     
39966     weight : '',
39967     
39968     indicatorpos : 'left',
39969     
39970     getAutoCreate : function()
39971     {
39972         var label = {
39973             tag : 'label',
39974             cls : 'roo-radio-set-label',
39975             cn : [
39976                 {
39977                     tag : 'span',
39978                     html : this.fieldLabel
39979                 }
39980             ]
39981         };
39982         if (Roo.bootstrap.version == 3) {
39983             
39984             
39985             if(this.indicatorpos == 'left'){
39986                 label.cn.unshift({
39987                     tag : 'i',
39988                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
39989                     tooltip : 'This field is required'
39990                 });
39991             } else {
39992                 label.cn.push({
39993                     tag : 'i',
39994                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
39995                     tooltip : 'This field is required'
39996                 });
39997             }
39998         }
39999         var items = {
40000             tag : 'div',
40001             cls : 'roo-radio-set-items'
40002         };
40003         
40004         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
40005         
40006         if (align === 'left' && this.fieldLabel.length) {
40007             
40008             items = {
40009                 cls : "roo-radio-set-right", 
40010                 cn: [
40011                     items
40012                 ]
40013             };
40014             
40015             if(this.labelWidth > 12){
40016                 label.style = "width: " + this.labelWidth + 'px';
40017             }
40018             
40019             if(this.labelWidth < 13 && this.labelmd == 0){
40020                 this.labelmd = this.labelWidth;
40021             }
40022             
40023             if(this.labellg > 0){
40024                 label.cls += ' col-lg-' + this.labellg;
40025                 items.cls += ' col-lg-' + (12 - this.labellg);
40026             }
40027             
40028             if(this.labelmd > 0){
40029                 label.cls += ' col-md-' + this.labelmd;
40030                 items.cls += ' col-md-' + (12 - this.labelmd);
40031             }
40032             
40033             if(this.labelsm > 0){
40034                 label.cls += ' col-sm-' + this.labelsm;
40035                 items.cls += ' col-sm-' + (12 - this.labelsm);
40036             }
40037             
40038             if(this.labelxs > 0){
40039                 label.cls += ' col-xs-' + this.labelxs;
40040                 items.cls += ' col-xs-' + (12 - this.labelxs);
40041             }
40042         }
40043         
40044         var cfg = {
40045             tag : 'div',
40046             cls : 'roo-radio-set',
40047             cn : [
40048                 {
40049                     tag : 'input',
40050                     cls : 'roo-radio-set-input',
40051                     type : 'hidden',
40052                     name : this.name,
40053                     value : this.value ? this.value :  ''
40054                 },
40055                 label,
40056                 items
40057             ]
40058         };
40059         
40060         if(this.weight.length){
40061             cfg.cls += ' roo-radio-' + this.weight;
40062         }
40063         
40064         if(this.inline) {
40065             cfg.cls += ' roo-radio-set-inline';
40066         }
40067         
40068         var settings=this;
40069         ['xs','sm','md','lg'].map(function(size){
40070             if (settings[size]) {
40071                 cfg.cls += ' col-' + size + '-' + settings[size];
40072             }
40073         });
40074         
40075         return cfg;
40076         
40077     },
40078
40079     initEvents : function()
40080     {
40081         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
40082         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
40083         
40084         if(!this.fieldLabel.length){
40085             this.labelEl.hide();
40086         }
40087         
40088         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
40089         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
40090         
40091         this.indicator = this.indicatorEl();
40092         
40093         if(this.indicator){
40094             this.indicator.addClass('invisible');
40095         }
40096         
40097         this.originalValue = this.getValue();
40098         
40099     },
40100     
40101     inputEl: function ()
40102     {
40103         return this.el.select('.roo-radio-set-input', true).first();
40104     },
40105     
40106     getChildContainer : function()
40107     {
40108         return this.itemsEl;
40109     },
40110     
40111     register : function(item)
40112     {
40113         this.radioes.push(item);
40114         
40115     },
40116     
40117     validate : function()
40118     {   
40119         if(this.getVisibilityEl().hasClass('hidden')){
40120             return true;
40121         }
40122         
40123         var valid = false;
40124         
40125         Roo.each(this.radioes, function(i){
40126             if(!i.checked){
40127                 return;
40128             }
40129             
40130             valid = true;
40131             return false;
40132         });
40133         
40134         if(this.allowBlank) {
40135             return true;
40136         }
40137         
40138         if(this.disabled || valid){
40139             this.markValid();
40140             return true;
40141         }
40142         
40143         this.markInvalid();
40144         return false;
40145         
40146     },
40147     
40148     markValid : function()
40149     {
40150         if(this.labelEl.isVisible(true) && this.indicatorEl()){
40151             this.indicatorEl().removeClass('visible');
40152             this.indicatorEl().addClass('invisible');
40153         }
40154         
40155         
40156         if (Roo.bootstrap.version == 3) {
40157             this.el.removeClass([this.invalidClass, this.validClass]);
40158             this.el.addClass(this.validClass);
40159         } else {
40160             this.el.removeClass(['is-invalid','is-valid']);
40161             this.el.addClass(['is-valid']);
40162         }
40163         this.fireEvent('valid', this);
40164     },
40165     
40166     markInvalid : function(msg)
40167     {
40168         if(this.allowBlank || this.disabled){
40169             return;
40170         }
40171         
40172         if(this.labelEl.isVisible(true) && this.indicatorEl()){
40173             this.indicatorEl().removeClass('invisible');
40174             this.indicatorEl().addClass('visible');
40175         }
40176         if (Roo.bootstrap.version == 3) {
40177             this.el.removeClass([this.invalidClass, this.validClass]);
40178             this.el.addClass(this.invalidClass);
40179         } else {
40180             this.el.removeClass(['is-invalid','is-valid']);
40181             this.el.addClass(['is-invalid']);
40182         }
40183         
40184         this.fireEvent('invalid', this, msg);
40185         
40186     },
40187     
40188     setValue : function(v, suppressEvent)
40189     {   
40190         if(this.value === v){
40191             return;
40192         }
40193         
40194         this.value = v;
40195         
40196         if(this.rendered){
40197             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40198         }
40199         
40200         Roo.each(this.radioes, function(i){
40201             i.checked = false;
40202             i.el.removeClass('checked');
40203         });
40204         
40205         Roo.each(this.radioes, function(i){
40206             
40207             if(i.value === v || i.value.toString() === v.toString()){
40208                 i.checked = true;
40209                 i.el.addClass('checked');
40210                 
40211                 if(suppressEvent !== true){
40212                     this.fireEvent('check', this, i);
40213                 }
40214                 
40215                 return false;
40216             }
40217             
40218         }, this);
40219         
40220         this.validate();
40221     },
40222     
40223     clearInvalid : function(){
40224         
40225         if(!this.el || this.preventMark){
40226             return;
40227         }
40228         
40229         this.el.removeClass([this.invalidClass]);
40230         
40231         this.fireEvent('valid', this);
40232     }
40233     
40234 });
40235
40236 Roo.apply(Roo.bootstrap.form.RadioSet, {
40237     
40238     groups: {},
40239     
40240     register : function(set)
40241     {
40242         this.groups[set.name] = set;
40243     },
40244     
40245     get: function(name) 
40246     {
40247         if (typeof(this.groups[name]) == 'undefined') {
40248             return false;
40249         }
40250         
40251         return this.groups[name] ;
40252     }
40253     
40254 });
40255 /*
40256  * Based on:
40257  * Ext JS Library 1.1.1
40258  * Copyright(c) 2006-2007, Ext JS, LLC.
40259  *
40260  * Originally Released Under LGPL - original licence link has changed is not relivant.
40261  *
40262  * Fork - LGPL
40263  * <script type="text/javascript">
40264  */
40265
40266
40267 /**
40268  * @class Roo.bootstrap.SplitBar
40269  * @extends Roo.util.Observable
40270  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
40271  * <br><br>
40272  * Usage:
40273  * <pre><code>
40274 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
40275                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
40276 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
40277 split.minSize = 100;
40278 split.maxSize = 600;
40279 split.animate = true;
40280 split.on('moved', splitterMoved);
40281 </code></pre>
40282  * @constructor
40283  * Create a new SplitBar
40284  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
40285  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
40286  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40287  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
40288                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
40289                         position of the SplitBar).
40290  */
40291 Roo.bootstrap.SplitBar = function(cfg){
40292     
40293     /** @private */
40294     
40295     //{
40296     //  dragElement : elm
40297     //  resizingElement: el,
40298         // optional..
40299     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
40300     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
40301         // existingProxy ???
40302     //}
40303     
40304     this.el = Roo.get(cfg.dragElement, true);
40305     this.el.dom.unselectable = "on";
40306     /** @private */
40307     this.resizingEl = Roo.get(cfg.resizingElement, true);
40308
40309     /**
40310      * @private
40311      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40312      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
40313      * @type Number
40314      */
40315     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
40316     
40317     /**
40318      * The minimum size of the resizing element. (Defaults to 0)
40319      * @type Number
40320      */
40321     this.minSize = 0;
40322     
40323     /**
40324      * The maximum size of the resizing element. (Defaults to 2000)
40325      * @type Number
40326      */
40327     this.maxSize = 2000;
40328     
40329     /**
40330      * Whether to animate the transition to the new size
40331      * @type Boolean
40332      */
40333     this.animate = false;
40334     
40335     /**
40336      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
40337      * @type Boolean
40338      */
40339     this.useShim = false;
40340     
40341     /** @private */
40342     this.shim = null;
40343     
40344     if(!cfg.existingProxy){
40345         /** @private */
40346         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
40347     }else{
40348         this.proxy = Roo.get(cfg.existingProxy).dom;
40349     }
40350     /** @private */
40351     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
40352     
40353     /** @private */
40354     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
40355     
40356     /** @private */
40357     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
40358     
40359     /** @private */
40360     this.dragSpecs = {};
40361     
40362     /**
40363      * @private The adapter to use to positon and resize elements
40364      */
40365     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40366     this.adapter.init(this);
40367     
40368     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40369         /** @private */
40370         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
40371         this.el.addClass("roo-splitbar-h");
40372     }else{
40373         /** @private */
40374         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
40375         this.el.addClass("roo-splitbar-v");
40376     }
40377     
40378     this.addEvents({
40379         /**
40380          * @event resize
40381          * Fires when the splitter is moved (alias for {@link #event-moved})
40382          * @param {Roo.bootstrap.SplitBar} this
40383          * @param {Number} newSize the new width or height
40384          */
40385         "resize" : true,
40386         /**
40387          * @event moved
40388          * Fires when the splitter is moved
40389          * @param {Roo.bootstrap.SplitBar} this
40390          * @param {Number} newSize the new width or height
40391          */
40392         "moved" : true,
40393         /**
40394          * @event beforeresize
40395          * Fires before the splitter is dragged
40396          * @param {Roo.bootstrap.SplitBar} this
40397          */
40398         "beforeresize" : true,
40399
40400         "beforeapply" : true
40401     });
40402
40403     Roo.util.Observable.call(this);
40404 };
40405
40406 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
40407     onStartProxyDrag : function(x, y){
40408         this.fireEvent("beforeresize", this);
40409         if(!this.overlay){
40410             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
40411             o.unselectable();
40412             o.enableDisplayMode("block");
40413             // all splitbars share the same overlay
40414             Roo.bootstrap.SplitBar.prototype.overlay = o;
40415         }
40416         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
40417         this.overlay.show();
40418         Roo.get(this.proxy).setDisplayed("block");
40419         var size = this.adapter.getElementSize(this);
40420         this.activeMinSize = this.getMinimumSize();;
40421         this.activeMaxSize = this.getMaximumSize();;
40422         var c1 = size - this.activeMinSize;
40423         var c2 = Math.max(this.activeMaxSize - size, 0);
40424         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40425             this.dd.resetConstraints();
40426             this.dd.setXConstraint(
40427                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
40428                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
40429             );
40430             this.dd.setYConstraint(0, 0);
40431         }else{
40432             this.dd.resetConstraints();
40433             this.dd.setXConstraint(0, 0);
40434             this.dd.setYConstraint(
40435                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
40436                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
40437             );
40438          }
40439         this.dragSpecs.startSize = size;
40440         this.dragSpecs.startPoint = [x, y];
40441         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
40442     },
40443     
40444     /** 
40445      * @private Called after the drag operation by the DDProxy
40446      */
40447     onEndProxyDrag : function(e){
40448         Roo.get(this.proxy).setDisplayed(false);
40449         var endPoint = Roo.lib.Event.getXY(e);
40450         if(this.overlay){
40451             this.overlay.hide();
40452         }
40453         var newSize;
40454         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40455             newSize = this.dragSpecs.startSize + 
40456                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
40457                     endPoint[0] - this.dragSpecs.startPoint[0] :
40458                     this.dragSpecs.startPoint[0] - endPoint[0]
40459                 );
40460         }else{
40461             newSize = this.dragSpecs.startSize + 
40462                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
40463                     endPoint[1] - this.dragSpecs.startPoint[1] :
40464                     this.dragSpecs.startPoint[1] - endPoint[1]
40465                 );
40466         }
40467         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
40468         if(newSize != this.dragSpecs.startSize){
40469             if(this.fireEvent('beforeapply', this, newSize) !== false){
40470                 this.adapter.setElementSize(this, newSize);
40471                 this.fireEvent("moved", this, newSize);
40472                 this.fireEvent("resize", this, newSize);
40473             }
40474         }
40475     },
40476     
40477     /**
40478      * Get the adapter this SplitBar uses
40479      * @return The adapter object
40480      */
40481     getAdapter : function(){
40482         return this.adapter;
40483     },
40484     
40485     /**
40486      * Set the adapter this SplitBar uses
40487      * @param {Object} adapter A SplitBar adapter object
40488      */
40489     setAdapter : function(adapter){
40490         this.adapter = adapter;
40491         this.adapter.init(this);
40492     },
40493     
40494     /**
40495      * Gets the minimum size for the resizing element
40496      * @return {Number} The minimum size
40497      */
40498     getMinimumSize : function(){
40499         return this.minSize;
40500     },
40501     
40502     /**
40503      * Sets the minimum size for the resizing element
40504      * @param {Number} minSize The minimum size
40505      */
40506     setMinimumSize : function(minSize){
40507         this.minSize = minSize;
40508     },
40509     
40510     /**
40511      * Gets the maximum size for the resizing element
40512      * @return {Number} The maximum size
40513      */
40514     getMaximumSize : function(){
40515         return this.maxSize;
40516     },
40517     
40518     /**
40519      * Sets the maximum size for the resizing element
40520      * @param {Number} maxSize The maximum size
40521      */
40522     setMaximumSize : function(maxSize){
40523         this.maxSize = maxSize;
40524     },
40525     
40526     /**
40527      * Sets the initialize size for the resizing element
40528      * @param {Number} size The initial size
40529      */
40530     setCurrentSize : function(size){
40531         var oldAnimate = this.animate;
40532         this.animate = false;
40533         this.adapter.setElementSize(this, size);
40534         this.animate = oldAnimate;
40535     },
40536     
40537     /**
40538      * Destroy this splitbar. 
40539      * @param {Boolean} removeEl True to remove the element
40540      */
40541     destroy : function(removeEl){
40542         if(this.shim){
40543             this.shim.remove();
40544         }
40545         this.dd.unreg();
40546         this.proxy.parentNode.removeChild(this.proxy);
40547         if(removeEl){
40548             this.el.remove();
40549         }
40550     }
40551 });
40552
40553 /**
40554  * @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.
40555  */
40556 Roo.bootstrap.SplitBar.createProxy = function(dir){
40557     var proxy = new Roo.Element(document.createElement("div"));
40558     proxy.unselectable();
40559     var cls = 'roo-splitbar-proxy';
40560     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
40561     document.body.appendChild(proxy.dom);
40562     return proxy.dom;
40563 };
40564
40565 /** 
40566  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
40567  * Default Adapter. It assumes the splitter and resizing element are not positioned
40568  * elements and only gets/sets the width of the element. Generally used for table based layouts.
40569  */
40570 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
40571 };
40572
40573 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
40574     // do nothing for now
40575     init : function(s){
40576     
40577     },
40578     /**
40579      * Called before drag operations to get the current size of the resizing element. 
40580      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40581      */
40582      getElementSize : function(s){
40583         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40584             return s.resizingEl.getWidth();
40585         }else{
40586             return s.resizingEl.getHeight();
40587         }
40588     },
40589     
40590     /**
40591      * Called after drag operations to set the size of the resizing element.
40592      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40593      * @param {Number} newSize The new size to set
40594      * @param {Function} onComplete A function to be invoked when resizing is complete
40595      */
40596     setElementSize : function(s, newSize, onComplete){
40597         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40598             if(!s.animate){
40599                 s.resizingEl.setWidth(newSize);
40600                 if(onComplete){
40601                     onComplete(s, newSize);
40602                 }
40603             }else{
40604                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
40605             }
40606         }else{
40607             
40608             if(!s.animate){
40609                 s.resizingEl.setHeight(newSize);
40610                 if(onComplete){
40611                     onComplete(s, newSize);
40612                 }
40613             }else{
40614                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
40615             }
40616         }
40617     }
40618 };
40619
40620 /** 
40621  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
40622  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
40623  * Adapter that  moves the splitter element to align with the resized sizing element. 
40624  * Used with an absolute positioned SplitBar.
40625  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
40626  * document.body, make sure you assign an id to the body element.
40627  */
40628 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
40629     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40630     this.container = Roo.get(container);
40631 };
40632
40633 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
40634     init : function(s){
40635         this.basic.init(s);
40636     },
40637     
40638     getElementSize : function(s){
40639         return this.basic.getElementSize(s);
40640     },
40641     
40642     setElementSize : function(s, newSize, onComplete){
40643         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
40644     },
40645     
40646     moveSplitter : function(s){
40647         var yes = Roo.bootstrap.SplitBar;
40648         switch(s.placement){
40649             case yes.LEFT:
40650                 s.el.setX(s.resizingEl.getRight());
40651                 break;
40652             case yes.RIGHT:
40653                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
40654                 break;
40655             case yes.TOP:
40656                 s.el.setY(s.resizingEl.getBottom());
40657                 break;
40658             case yes.BOTTOM:
40659                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
40660                 break;
40661         }
40662     }
40663 };
40664
40665 /**
40666  * Orientation constant - Create a vertical SplitBar
40667  * @static
40668  * @type Number
40669  */
40670 Roo.bootstrap.SplitBar.VERTICAL = 1;
40671
40672 /**
40673  * Orientation constant - Create a horizontal SplitBar
40674  * @static
40675  * @type Number
40676  */
40677 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
40678
40679 /**
40680  * Placement constant - The resizing element is to the left of the splitter element
40681  * @static
40682  * @type Number
40683  */
40684 Roo.bootstrap.SplitBar.LEFT = 1;
40685
40686 /**
40687  * Placement constant - The resizing element is to the right of the splitter element
40688  * @static
40689  * @type Number
40690  */
40691 Roo.bootstrap.SplitBar.RIGHT = 2;
40692
40693 /**
40694  * Placement constant - The resizing element is positioned above the splitter element
40695  * @static
40696  * @type Number
40697  */
40698 Roo.bootstrap.SplitBar.TOP = 3;
40699
40700 /**
40701  * Placement constant - The resizing element is positioned under splitter element
40702  * @static
40703  * @type Number
40704  */
40705 Roo.bootstrap.SplitBar.BOTTOM = 4;
40706 /*
40707  * Based on:
40708  * Ext JS Library 1.1.1
40709  * Copyright(c) 2006-2007, Ext JS, LLC.
40710  *
40711  * Originally Released Under LGPL - original licence link has changed is not relivant.
40712  *
40713  * Fork - LGPL
40714  * <script type="text/javascript">
40715  */
40716
40717 /**
40718  * @class Roo.bootstrap.layout.Manager
40719  * @extends Roo.bootstrap.Component
40720  * @abstract
40721  * Base class for layout managers.
40722  */
40723 Roo.bootstrap.layout.Manager = function(config)
40724 {
40725     this.monitorWindowResize = true; // do this before we apply configuration.
40726     
40727     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
40728
40729
40730
40731
40732
40733     /** false to disable window resize monitoring @type Boolean */
40734     
40735     this.regions = {};
40736     this.addEvents({
40737         /**
40738          * @event layout
40739          * Fires when a layout is performed.
40740          * @param {Roo.LayoutManager} this
40741          */
40742         "layout" : true,
40743         /**
40744          * @event regionresized
40745          * Fires when the user resizes a region.
40746          * @param {Roo.LayoutRegion} region The resized region
40747          * @param {Number} newSize The new size (width for east/west, height for north/south)
40748          */
40749         "regionresized" : true,
40750         /**
40751          * @event regioncollapsed
40752          * Fires when a region is collapsed.
40753          * @param {Roo.LayoutRegion} region The collapsed region
40754          */
40755         "regioncollapsed" : true,
40756         /**
40757          * @event regionexpanded
40758          * Fires when a region is expanded.
40759          * @param {Roo.LayoutRegion} region The expanded region
40760          */
40761         "regionexpanded" : true
40762     });
40763     this.updating = false;
40764
40765     if (config.el) {
40766         this.el = Roo.get(config.el);
40767         this.initEvents();
40768     }
40769
40770 };
40771
40772 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
40773
40774
40775     regions : null,
40776
40777     monitorWindowResize : true,
40778
40779
40780     updating : false,
40781
40782
40783     onRender : function(ct, position)
40784     {
40785         if(!this.el){
40786             this.el = Roo.get(ct);
40787             this.initEvents();
40788         }
40789         //this.fireEvent('render',this);
40790     },
40791
40792
40793     initEvents: function()
40794     {
40795
40796
40797         // ie scrollbar fix
40798         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
40799             document.body.scroll = "no";
40800         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
40801             this.el.position('relative');
40802         }
40803         this.id = this.el.id;
40804         this.el.addClass("roo-layout-container");
40805         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
40806         if(this.el.dom != document.body ) {
40807             this.el.on('resize', this.layout,this);
40808             this.el.on('show', this.layout,this);
40809         }
40810
40811     },
40812
40813     /**
40814      * Returns true if this layout is currently being updated
40815      * @return {Boolean}
40816      */
40817     isUpdating : function(){
40818         return this.updating;
40819     },
40820
40821     /**
40822      * Suspend the LayoutManager from doing auto-layouts while
40823      * making multiple add or remove calls
40824      */
40825     beginUpdate : function(){
40826         this.updating = true;
40827     },
40828
40829     /**
40830      * Restore auto-layouts and optionally disable the manager from performing a layout
40831      * @param {Boolean} noLayout true to disable a layout update
40832      */
40833     endUpdate : function(noLayout){
40834         this.updating = false;
40835         if(!noLayout){
40836             this.layout();
40837         }
40838     },
40839
40840     layout: function(){
40841         // abstract...
40842     },
40843
40844     onRegionResized : function(region, newSize){
40845         this.fireEvent("regionresized", region, newSize);
40846         this.layout();
40847     },
40848
40849     onRegionCollapsed : function(region){
40850         this.fireEvent("regioncollapsed", region);
40851     },
40852
40853     onRegionExpanded : function(region){
40854         this.fireEvent("regionexpanded", region);
40855     },
40856
40857     /**
40858      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
40859      * performs box-model adjustments.
40860      * @return {Object} The size as an object {width: (the width), height: (the height)}
40861      */
40862     getViewSize : function()
40863     {
40864         var size;
40865         if(this.el.dom != document.body){
40866             size = this.el.getSize();
40867         }else{
40868             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
40869         }
40870         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
40871         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40872         return size;
40873     },
40874
40875     /**
40876      * Returns the Element this layout is bound to.
40877      * @return {Roo.Element}
40878      */
40879     getEl : function(){
40880         return this.el;
40881     },
40882
40883     /**
40884      * Returns the specified region.
40885      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
40886      * @return {Roo.LayoutRegion}
40887      */
40888     getRegion : function(target){
40889         return this.regions[target.toLowerCase()];
40890     },
40891
40892     onWindowResize : function(){
40893         if(this.monitorWindowResize){
40894             this.layout();
40895         }
40896     }
40897 });
40898 /*
40899  * Based on:
40900  * Ext JS Library 1.1.1
40901  * Copyright(c) 2006-2007, Ext JS, LLC.
40902  *
40903  * Originally Released Under LGPL - original licence link has changed is not relivant.
40904  *
40905  * Fork - LGPL
40906  * <script type="text/javascript">
40907  */
40908 /**
40909  * @class Roo.bootstrap.layout.Border
40910  * @extends Roo.bootstrap.layout.Manager
40911  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
40912  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
40913  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
40914  * please see: examples/bootstrap/nested.html<br><br>
40915  
40916 <b>The container the layout is rendered into can be either the body element or any other element.
40917 If it is not the body element, the container needs to either be an absolute positioned element,
40918 or you will need to add "position:relative" to the css of the container.  You will also need to specify
40919 the container size if it is not the body element.</b>
40920
40921 * @constructor
40922 * Create a new Border
40923 * @param {Object} config Configuration options
40924  */
40925 Roo.bootstrap.layout.Border = function(config){
40926     config = config || {};
40927     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
40928     
40929     
40930     
40931     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40932         if(config[region]){
40933             config[region].region = region;
40934             this.addRegion(config[region]);
40935         }
40936     },this);
40937     
40938 };
40939
40940 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
40941
40942 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
40943     
40944         /**
40945          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
40946          */
40947         /**
40948          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
40949          */
40950         /**
40951          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
40952          */
40953         /**
40954          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
40955          */
40956         /**
40957          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
40958          */
40959         
40960         
40961         
40962         
40963     parent : false, // this might point to a 'nest' or a ???
40964     
40965     /**
40966      * Creates and adds a new region if it doesn't already exist.
40967      * @param {String} target The target region key (north, south, east, west or center).
40968      * @param {Object} config The regions config object
40969      * @return {BorderLayoutRegion} The new region
40970      */
40971     addRegion : function(config)
40972     {
40973         if(!this.regions[config.region]){
40974             var r = this.factory(config);
40975             this.bindRegion(r);
40976         }
40977         return this.regions[config.region];
40978     },
40979
40980     // private (kinda)
40981     bindRegion : function(r){
40982         this.regions[r.config.region] = r;
40983         
40984         r.on("visibilitychange",    this.layout, this);
40985         r.on("paneladded",          this.layout, this);
40986         r.on("panelremoved",        this.layout, this);
40987         r.on("invalidated",         this.layout, this);
40988         r.on("resized",             this.onRegionResized, this);
40989         r.on("collapsed",           this.onRegionCollapsed, this);
40990         r.on("expanded",            this.onRegionExpanded, this);
40991     },
40992
40993     /**
40994      * Performs a layout update.
40995      */
40996     layout : function()
40997     {
40998         if(this.updating) {
40999             return;
41000         }
41001         
41002         // render all the rebions if they have not been done alreayd?
41003         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
41004             if(this.regions[region] && !this.regions[region].bodyEl){
41005                 this.regions[region].onRender(this.el)
41006             }
41007         },this);
41008         
41009         var size = this.getViewSize();
41010         var w = size.width;
41011         var h = size.height;
41012         var centerW = w;
41013         var centerH = h;
41014         var centerY = 0;
41015         var centerX = 0;
41016         //var x = 0, y = 0;
41017
41018         var rs = this.regions;
41019         var north = rs["north"];
41020         var south = rs["south"]; 
41021         var west = rs["west"];
41022         var east = rs["east"];
41023         var center = rs["center"];
41024         //if(this.hideOnLayout){ // not supported anymore
41025             //c.el.setStyle("display", "none");
41026         //}
41027         if(north && north.isVisible()){
41028             var b = north.getBox();
41029             var m = north.getMargins();
41030             b.width = w - (m.left+m.right);
41031             b.x = m.left;
41032             b.y = m.top;
41033             centerY = b.height + b.y + m.bottom;
41034             centerH -= centerY;
41035             north.updateBox(this.safeBox(b));
41036         }
41037         if(south && south.isVisible()){
41038             var b = south.getBox();
41039             var m = south.getMargins();
41040             b.width = w - (m.left+m.right);
41041             b.x = m.left;
41042             var totalHeight = (b.height + m.top + m.bottom);
41043             b.y = h - totalHeight + m.top;
41044             centerH -= totalHeight;
41045             south.updateBox(this.safeBox(b));
41046         }
41047         if(west && west.isVisible()){
41048             var b = west.getBox();
41049             var m = west.getMargins();
41050             b.height = centerH - (m.top+m.bottom);
41051             b.x = m.left;
41052             b.y = centerY + m.top;
41053             var totalWidth = (b.width + m.left + m.right);
41054             centerX += totalWidth;
41055             centerW -= totalWidth;
41056             west.updateBox(this.safeBox(b));
41057         }
41058         if(east && east.isVisible()){
41059             var b = east.getBox();
41060             var m = east.getMargins();
41061             b.height = centerH - (m.top+m.bottom);
41062             var totalWidth = (b.width + m.left + m.right);
41063             b.x = w - totalWidth + m.left;
41064             b.y = centerY + m.top;
41065             centerW -= totalWidth;
41066             east.updateBox(this.safeBox(b));
41067         }
41068         if(center){
41069             var m = center.getMargins();
41070             var centerBox = {
41071                 x: centerX + m.left,
41072                 y: centerY + m.top,
41073                 width: centerW - (m.left+m.right),
41074                 height: centerH - (m.top+m.bottom)
41075             };
41076             //if(this.hideOnLayout){
41077                 //center.el.setStyle("display", "block");
41078             //}
41079             center.updateBox(this.safeBox(centerBox));
41080         }
41081         this.el.repaint();
41082         this.fireEvent("layout", this);
41083     },
41084
41085     // private
41086     safeBox : function(box){
41087         box.width = Math.max(0, box.width);
41088         box.height = Math.max(0, box.height);
41089         return box;
41090     },
41091
41092     /**
41093      * Adds a ContentPanel (or subclass) to this layout.
41094      * @param {String} target The target region key (north, south, east, west or center).
41095      * @param {Roo.ContentPanel} panel The panel to add
41096      * @return {Roo.ContentPanel} The added panel
41097      */
41098     add : function(target, panel){
41099          
41100         target = target.toLowerCase();
41101         return this.regions[target].add(panel);
41102     },
41103
41104     /**
41105      * Remove a ContentPanel (or subclass) to this layout.
41106      * @param {String} target The target region key (north, south, east, west or center).
41107      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
41108      * @return {Roo.ContentPanel} The removed panel
41109      */
41110     remove : function(target, panel){
41111         target = target.toLowerCase();
41112         return this.regions[target].remove(panel);
41113     },
41114
41115     /**
41116      * Searches all regions for a panel with the specified id
41117      * @param {String} panelId
41118      * @return {Roo.ContentPanel} The panel or null if it wasn't found
41119      */
41120     findPanel : function(panelId){
41121         var rs = this.regions;
41122         for(var target in rs){
41123             if(typeof rs[target] != "function"){
41124                 var p = rs[target].getPanel(panelId);
41125                 if(p){
41126                     return p;
41127                 }
41128             }
41129         }
41130         return null;
41131     },
41132
41133     /**
41134      * Searches all regions for a panel with the specified id and activates (shows) it.
41135      * @param {String/ContentPanel} panelId The panels id or the panel itself
41136      * @return {Roo.ContentPanel} The shown panel or null
41137      */
41138     showPanel : function(panelId) {
41139       var rs = this.regions;
41140       for(var target in rs){
41141          var r = rs[target];
41142          if(typeof r != "function"){
41143             if(r.hasPanel(panelId)){
41144                return r.showPanel(panelId);
41145             }
41146          }
41147       }
41148       return null;
41149    },
41150
41151    /**
41152      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
41153      * @param {Roo.state.Provider} provider (optional) An alternate state provider
41154      */
41155    /*
41156     restoreState : function(provider){
41157         if(!provider){
41158             provider = Roo.state.Manager;
41159         }
41160         var sm = new Roo.LayoutStateManager();
41161         sm.init(this, provider);
41162     },
41163 */
41164  
41165  
41166     /**
41167      * Adds a xtype elements to the layout.
41168      * <pre><code>
41169
41170 layout.addxtype({
41171        xtype : 'ContentPanel',
41172        region: 'west',
41173        items: [ .... ]
41174    }
41175 );
41176
41177 layout.addxtype({
41178         xtype : 'NestedLayoutPanel',
41179         region: 'west',
41180         layout: {
41181            center: { },
41182            west: { }   
41183         },
41184         items : [ ... list of content panels or nested layout panels.. ]
41185    }
41186 );
41187 </code></pre>
41188      * @param {Object} cfg Xtype definition of item to add.
41189      */
41190     addxtype : function(cfg)
41191     {
41192         // basically accepts a pannel...
41193         // can accept a layout region..!?!?
41194         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
41195         
41196         
41197         // theory?  children can only be panels??
41198         
41199         //if (!cfg.xtype.match(/Panel$/)) {
41200         //    return false;
41201         //}
41202         var ret = false;
41203         
41204         if (typeof(cfg.region) == 'undefined') {
41205             Roo.log("Failed to add Panel, region was not set");
41206             Roo.log(cfg);
41207             return false;
41208         }
41209         var region = cfg.region;
41210         delete cfg.region;
41211         
41212           
41213         var xitems = [];
41214         if (cfg.items) {
41215             xitems = cfg.items;
41216             delete cfg.items;
41217         }
41218         var nb = false;
41219         
41220         if ( region == 'center') {
41221             Roo.log("Center: " + cfg.title);
41222         }
41223         
41224         
41225         switch(cfg.xtype) 
41226         {
41227             case 'Content':  // ContentPanel (el, cfg)
41228             case 'Scroll':  // ContentPanel (el, cfg)
41229             case 'View': 
41230                 cfg.autoCreate = cfg.autoCreate || true;
41231                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41232                 //} else {
41233                 //    var el = this.el.createChild();
41234                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
41235                 //}
41236                 
41237                 this.add(region, ret);
41238                 break;
41239             
41240             /*
41241             case 'TreePanel': // our new panel!
41242                 cfg.el = this.el.createChild();
41243                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41244                 this.add(region, ret);
41245                 break;
41246             */
41247             
41248             case 'Nest': 
41249                 // create a new Layout (which is  a Border Layout...
41250                 
41251                 var clayout = cfg.layout;
41252                 clayout.el  = this.el.createChild();
41253                 clayout.items   = clayout.items  || [];
41254                 
41255                 delete cfg.layout;
41256                 
41257                 // replace this exitems with the clayout ones..
41258                 xitems = clayout.items;
41259                  
41260                 // force background off if it's in center...
41261                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
41262                     cfg.background = false;
41263                 }
41264                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
41265                 
41266                 
41267                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41268                 //console.log('adding nested layout panel '  + cfg.toSource());
41269                 this.add(region, ret);
41270                 nb = {}; /// find first...
41271                 break;
41272             
41273             case 'Grid':
41274                 
41275                 // needs grid and region
41276                 
41277                 //var el = this.getRegion(region).el.createChild();
41278                 /*
41279                  *var el = this.el.createChild();
41280                 // create the grid first...
41281                 cfg.grid.container = el;
41282                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
41283                 */
41284                 
41285                 if (region == 'center' && this.active ) {
41286                     cfg.background = false;
41287                 }
41288                 
41289                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41290                 
41291                 this.add(region, ret);
41292                 /*
41293                 if (cfg.background) {
41294                     // render grid on panel activation (if panel background)
41295                     ret.on('activate', function(gp) {
41296                         if (!gp.grid.rendered) {
41297                     //        gp.grid.render(el);
41298                         }
41299                     });
41300                 } else {
41301                   //  cfg.grid.render(el);
41302                 }
41303                 */
41304                 break;
41305            
41306            
41307             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
41308                 // it was the old xcomponent building that caused this before.
41309                 // espeically if border is the top element in the tree.
41310                 ret = this;
41311                 break; 
41312                 
41313                     
41314                 
41315                 
41316                 
41317             default:
41318                 /*
41319                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
41320                     
41321                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41322                     this.add(region, ret);
41323                 } else {
41324                 */
41325                     Roo.log(cfg);
41326                     throw "Can not add '" + cfg.xtype + "' to Border";
41327                     return null;
41328              
41329                                 
41330              
41331         }
41332         this.beginUpdate();
41333         // add children..
41334         var region = '';
41335         var abn = {};
41336         Roo.each(xitems, function(i)  {
41337             region = nb && i.region ? i.region : false;
41338             
41339             var add = ret.addxtype(i);
41340            
41341             if (region) {
41342                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
41343                 if (!i.background) {
41344                     abn[region] = nb[region] ;
41345                 }
41346             }
41347             
41348         });
41349         this.endUpdate();
41350
41351         // make the last non-background panel active..
41352         //if (nb) { Roo.log(abn); }
41353         if (nb) {
41354             
41355             for(var r in abn) {
41356                 region = this.getRegion(r);
41357                 if (region) {
41358                     // tried using nb[r], but it does not work..
41359                      
41360                     region.showPanel(abn[r]);
41361                    
41362                 }
41363             }
41364         }
41365         return ret;
41366         
41367     },
41368     
41369     
41370 // private
41371     factory : function(cfg)
41372     {
41373         
41374         var validRegions = Roo.bootstrap.layout.Border.regions;
41375
41376         var target = cfg.region;
41377         cfg.mgr = this;
41378         
41379         var r = Roo.bootstrap.layout;
41380         Roo.log(target);
41381         switch(target){
41382             case "north":
41383                 return new r.North(cfg);
41384             case "south":
41385                 return new r.South(cfg);
41386             case "east":
41387                 return new r.East(cfg);
41388             case "west":
41389                 return new r.West(cfg);
41390             case "center":
41391                 return new r.Center(cfg);
41392         }
41393         throw 'Layout region "'+target+'" not supported.';
41394     }
41395     
41396     
41397 });
41398  /*
41399  * Based on:
41400  * Ext JS Library 1.1.1
41401  * Copyright(c) 2006-2007, Ext JS, LLC.
41402  *
41403  * Originally Released Under LGPL - original licence link has changed is not relivant.
41404  *
41405  * Fork - LGPL
41406  * <script type="text/javascript">
41407  */
41408  
41409 /**
41410  * @class Roo.bootstrap.layout.Basic
41411  * @extends Roo.util.Observable
41412  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
41413  * and does not have a titlebar, tabs or any other features. All it does is size and position 
41414  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
41415  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41416  * @cfg {string}   region  the region that it inhabits..
41417  * @cfg {bool}   skipConfig skip config?
41418  * 
41419
41420  */
41421 Roo.bootstrap.layout.Basic = function(config){
41422     
41423     this.mgr = config.mgr;
41424     
41425     this.position = config.region;
41426     
41427     var skipConfig = config.skipConfig;
41428     
41429     this.events = {
41430         /**
41431          * @scope Roo.BasicLayoutRegion
41432          */
41433         
41434         /**
41435          * @event beforeremove
41436          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
41437          * @param {Roo.LayoutRegion} this
41438          * @param {Roo.ContentPanel} panel The panel
41439          * @param {Object} e The cancel event object
41440          */
41441         "beforeremove" : true,
41442         /**
41443          * @event invalidated
41444          * Fires when the layout for this region is changed.
41445          * @param {Roo.LayoutRegion} this
41446          */
41447         "invalidated" : true,
41448         /**
41449          * @event visibilitychange
41450          * Fires when this region is shown or hidden 
41451          * @param {Roo.LayoutRegion} this
41452          * @param {Boolean} visibility true or false
41453          */
41454         "visibilitychange" : true,
41455         /**
41456          * @event paneladded
41457          * Fires when a panel is added. 
41458          * @param {Roo.LayoutRegion} this
41459          * @param {Roo.ContentPanel} panel The panel
41460          */
41461         "paneladded" : true,
41462         /**
41463          * @event panelremoved
41464          * Fires when a panel is removed. 
41465          * @param {Roo.LayoutRegion} this
41466          * @param {Roo.ContentPanel} panel The panel
41467          */
41468         "panelremoved" : true,
41469         /**
41470          * @event beforecollapse
41471          * Fires when this region before collapse.
41472          * @param {Roo.LayoutRegion} this
41473          */
41474         "beforecollapse" : true,
41475         /**
41476          * @event collapsed
41477          * Fires when this region is collapsed.
41478          * @param {Roo.LayoutRegion} this
41479          */
41480         "collapsed" : true,
41481         /**
41482          * @event expanded
41483          * Fires when this region is expanded.
41484          * @param {Roo.LayoutRegion} this
41485          */
41486         "expanded" : true,
41487         /**
41488          * @event slideshow
41489          * Fires when this region is slid into view.
41490          * @param {Roo.LayoutRegion} this
41491          */
41492         "slideshow" : true,
41493         /**
41494          * @event slidehide
41495          * Fires when this region slides out of view. 
41496          * @param {Roo.LayoutRegion} this
41497          */
41498         "slidehide" : true,
41499         /**
41500          * @event panelactivated
41501          * Fires when a panel is activated. 
41502          * @param {Roo.LayoutRegion} this
41503          * @param {Roo.ContentPanel} panel The activated panel
41504          */
41505         "panelactivated" : true,
41506         /**
41507          * @event resized
41508          * Fires when the user resizes this region. 
41509          * @param {Roo.LayoutRegion} this
41510          * @param {Number} newSize The new size (width for east/west, height for north/south)
41511          */
41512         "resized" : true
41513     };
41514     /** A collection of panels in this region. @type Roo.util.MixedCollection */
41515     this.panels = new Roo.util.MixedCollection();
41516     this.panels.getKey = this.getPanelId.createDelegate(this);
41517     this.box = null;
41518     this.activePanel = null;
41519     // ensure listeners are added...
41520     
41521     if (config.listeners || config.events) {
41522         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
41523             listeners : config.listeners || {},
41524             events : config.events || {}
41525         });
41526     }
41527     
41528     if(skipConfig !== true){
41529         this.applyConfig(config);
41530     }
41531 };
41532
41533 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
41534 {
41535     getPanelId : function(p){
41536         return p.getId();
41537     },
41538     
41539     applyConfig : function(config){
41540         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41541         this.config = config;
41542         
41543     },
41544     
41545     /**
41546      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
41547      * the width, for horizontal (north, south) the height.
41548      * @param {Number} newSize The new width or height
41549      */
41550     resizeTo : function(newSize){
41551         var el = this.el ? this.el :
41552                  (this.activePanel ? this.activePanel.getEl() : null);
41553         if(el){
41554             switch(this.position){
41555                 case "east":
41556                 case "west":
41557                     el.setWidth(newSize);
41558                     this.fireEvent("resized", this, newSize);
41559                 break;
41560                 case "north":
41561                 case "south":
41562                     el.setHeight(newSize);
41563                     this.fireEvent("resized", this, newSize);
41564                 break;                
41565             }
41566         }
41567     },
41568     
41569     getBox : function(){
41570         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
41571     },
41572     
41573     getMargins : function(){
41574         return this.margins;
41575     },
41576     
41577     updateBox : function(box){
41578         this.box = box;
41579         var el = this.activePanel.getEl();
41580         el.dom.style.left = box.x + "px";
41581         el.dom.style.top = box.y + "px";
41582         this.activePanel.setSize(box.width, box.height);
41583     },
41584     
41585     /**
41586      * Returns the container element for this region.
41587      * @return {Roo.Element}
41588      */
41589     getEl : function(){
41590         return this.activePanel;
41591     },
41592     
41593     /**
41594      * Returns true if this region is currently visible.
41595      * @return {Boolean}
41596      */
41597     isVisible : function(){
41598         return this.activePanel ? true : false;
41599     },
41600     
41601     setActivePanel : function(panel){
41602         panel = this.getPanel(panel);
41603         if(this.activePanel && this.activePanel != panel){
41604             this.activePanel.setActiveState(false);
41605             this.activePanel.getEl().setLeftTop(-10000,-10000);
41606         }
41607         this.activePanel = panel;
41608         panel.setActiveState(true);
41609         if(this.box){
41610             panel.setSize(this.box.width, this.box.height);
41611         }
41612         this.fireEvent("panelactivated", this, panel);
41613         this.fireEvent("invalidated");
41614     },
41615     
41616     /**
41617      * Show the specified panel.
41618      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
41619      * @return {Roo.ContentPanel} The shown panel or null
41620      */
41621     showPanel : function(panel){
41622         panel = this.getPanel(panel);
41623         if(panel){
41624             this.setActivePanel(panel);
41625         }
41626         return panel;
41627     },
41628     
41629     /**
41630      * Get the active panel for this region.
41631      * @return {Roo.ContentPanel} The active panel or null
41632      */
41633     getActivePanel : function(){
41634         return this.activePanel;
41635     },
41636     
41637     /**
41638      * Add the passed ContentPanel(s)
41639      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
41640      * @return {Roo.ContentPanel} The panel added (if only one was added)
41641      */
41642     add : function(panel){
41643         if(arguments.length > 1){
41644             for(var i = 0, len = arguments.length; i < len; i++) {
41645                 this.add(arguments[i]);
41646             }
41647             return null;
41648         }
41649         if(this.hasPanel(panel)){
41650             this.showPanel(panel);
41651             return panel;
41652         }
41653         var el = panel.getEl();
41654         if(el.dom.parentNode != this.mgr.el.dom){
41655             this.mgr.el.dom.appendChild(el.dom);
41656         }
41657         if(panel.setRegion){
41658             panel.setRegion(this);
41659         }
41660         this.panels.add(panel);
41661         el.setStyle("position", "absolute");
41662         if(!panel.background){
41663             this.setActivePanel(panel);
41664             if(this.config.initialSize && this.panels.getCount()==1){
41665                 this.resizeTo(this.config.initialSize);
41666             }
41667         }
41668         this.fireEvent("paneladded", this, panel);
41669         return panel;
41670     },
41671     
41672     /**
41673      * Returns true if the panel is in this region.
41674      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41675      * @return {Boolean}
41676      */
41677     hasPanel : function(panel){
41678         if(typeof panel == "object"){ // must be panel obj
41679             panel = panel.getId();
41680         }
41681         return this.getPanel(panel) ? true : false;
41682     },
41683     
41684     /**
41685      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
41686      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41687      * @param {Boolean} preservePanel Overrides the config preservePanel option
41688      * @return {Roo.ContentPanel} The panel that was removed
41689      */
41690     remove : function(panel, preservePanel){
41691         panel = this.getPanel(panel);
41692         if(!panel){
41693             return null;
41694         }
41695         var e = {};
41696         this.fireEvent("beforeremove", this, panel, e);
41697         if(e.cancel === true){
41698             return null;
41699         }
41700         var panelId = panel.getId();
41701         this.panels.removeKey(panelId);
41702         return panel;
41703     },
41704     
41705     /**
41706      * Returns the panel specified or null if it's not in this region.
41707      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41708      * @return {Roo.ContentPanel}
41709      */
41710     getPanel : function(id){
41711         if(typeof id == "object"){ // must be panel obj
41712             return id;
41713         }
41714         return this.panels.get(id);
41715     },
41716     
41717     /**
41718      * Returns this regions position (north/south/east/west/center).
41719      * @return {String} 
41720      */
41721     getPosition: function(){
41722         return this.position;    
41723     }
41724 });/*
41725  * Based on:
41726  * Ext JS Library 1.1.1
41727  * Copyright(c) 2006-2007, Ext JS, LLC.
41728  *
41729  * Originally Released Under LGPL - original licence link has changed is not relivant.
41730  *
41731  * Fork - LGPL
41732  * <script type="text/javascript">
41733  */
41734  
41735 /**
41736  * @class Roo.bootstrap.layout.Region
41737  * @extends Roo.bootstrap.layout.Basic
41738  * This class represents a region in a layout manager.
41739  
41740  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
41741  * @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})
41742  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
41743  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
41744  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
41745  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
41746  * @cfg {String}    title           The title for the region (overrides panel titles)
41747  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
41748  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
41749  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
41750  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
41751  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
41752  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
41753  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
41754  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
41755  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
41756  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
41757
41758  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
41759  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
41760  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
41761  * @cfg {Number}    width           For East/West panels
41762  * @cfg {Number}    height          For North/South panels
41763  * @cfg {Boolean}   split           To show the splitter
41764  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
41765  * 
41766  * @cfg {string}   cls             Extra CSS classes to add to region
41767  * 
41768  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41769  * @cfg {string}   region  the region that it inhabits..
41770  *
41771
41772  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
41773  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
41774
41775  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
41776  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
41777  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
41778  */
41779 Roo.bootstrap.layout.Region = function(config)
41780 {
41781     this.applyConfig(config);
41782
41783     var mgr = config.mgr;
41784     var pos = config.region;
41785     config.skipConfig = true;
41786     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
41787     
41788     if (mgr.el) {
41789         this.onRender(mgr.el);   
41790     }
41791      
41792     this.visible = true;
41793     this.collapsed = false;
41794     this.unrendered_panels = [];
41795 };
41796
41797 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
41798
41799     position: '', // set by wrapper (eg. north/south etc..)
41800     unrendered_panels : null,  // unrendered panels.
41801     
41802     tabPosition : false,
41803     
41804     mgr: false, // points to 'Border'
41805     
41806     
41807     createBody : function(){
41808         /** This region's body element 
41809         * @type Roo.Element */
41810         this.bodyEl = this.el.createChild({
41811                 tag: "div",
41812                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
41813         });
41814     },
41815
41816     onRender: function(ctr, pos)
41817     {
41818         var dh = Roo.DomHelper;
41819         /** This region's container element 
41820         * @type Roo.Element */
41821         this.el = dh.append(ctr.dom, {
41822                 tag: "div",
41823                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
41824             }, true);
41825         /** This region's title element 
41826         * @type Roo.Element */
41827     
41828         this.titleEl = dh.append(this.el.dom,  {
41829                 tag: "div",
41830                 unselectable: "on",
41831                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
41832                 children:[
41833                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
41834                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
41835                 ]
41836             }, true);
41837         
41838         this.titleEl.enableDisplayMode();
41839         /** This region's title text element 
41840         * @type HTMLElement */
41841         this.titleTextEl = this.titleEl.dom.firstChild;
41842         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
41843         /*
41844         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
41845         this.closeBtn.enableDisplayMode();
41846         this.closeBtn.on("click", this.closeClicked, this);
41847         this.closeBtn.hide();
41848     */
41849         this.createBody(this.config);
41850         if(this.config.hideWhenEmpty){
41851             this.hide();
41852             this.on("paneladded", this.validateVisibility, this);
41853             this.on("panelremoved", this.validateVisibility, this);
41854         }
41855         if(this.autoScroll){
41856             this.bodyEl.setStyle("overflow", "auto");
41857         }else{
41858             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
41859         }
41860         //if(c.titlebar !== false){
41861             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
41862                 this.titleEl.hide();
41863             }else{
41864                 this.titleEl.show();
41865                 if(this.config.title){
41866                     this.titleTextEl.innerHTML = this.config.title;
41867                 }
41868             }
41869         //}
41870         if(this.config.collapsed){
41871             this.collapse(true);
41872         }
41873         if(this.config.hidden){
41874             this.hide();
41875         }
41876         
41877         if (this.unrendered_panels && this.unrendered_panels.length) {
41878             for (var i =0;i< this.unrendered_panels.length; i++) {
41879                 this.add(this.unrendered_panels[i]);
41880             }
41881             this.unrendered_panels = null;
41882             
41883         }
41884         
41885     },
41886     
41887     applyConfig : function(c)
41888     {
41889         /*
41890          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
41891             var dh = Roo.DomHelper;
41892             if(c.titlebar !== false){
41893                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
41894                 this.collapseBtn.on("click", this.collapse, this);
41895                 this.collapseBtn.enableDisplayMode();
41896                 /*
41897                 if(c.showPin === true || this.showPin){
41898                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
41899                     this.stickBtn.enableDisplayMode();
41900                     this.stickBtn.on("click", this.expand, this);
41901                     this.stickBtn.hide();
41902                 }
41903                 
41904             }
41905             */
41906             /** This region's collapsed element
41907             * @type Roo.Element */
41908             /*
41909              *
41910             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
41911                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
41912             ]}, true);
41913             
41914             if(c.floatable !== false){
41915                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
41916                this.collapsedEl.on("click", this.collapseClick, this);
41917             }
41918
41919             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
41920                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
41921                    id: "message", unselectable: "on", style:{"float":"left"}});
41922                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
41923              }
41924             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
41925             this.expandBtn.on("click", this.expand, this);
41926             
41927         }
41928         
41929         if(this.collapseBtn){
41930             this.collapseBtn.setVisible(c.collapsible == true);
41931         }
41932         
41933         this.cmargins = c.cmargins || this.cmargins ||
41934                          (this.position == "west" || this.position == "east" ?
41935                              {top: 0, left: 2, right:2, bottom: 0} :
41936                              {top: 2, left: 0, right:0, bottom: 2});
41937         */
41938         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41939         
41940         
41941         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
41942         
41943         this.autoScroll = c.autoScroll || false;
41944         
41945         
41946        
41947         
41948         this.duration = c.duration || .30;
41949         this.slideDuration = c.slideDuration || .45;
41950         this.config = c;
41951        
41952     },
41953     /**
41954      * Returns true if this region is currently visible.
41955      * @return {Boolean}
41956      */
41957     isVisible : function(){
41958         return this.visible;
41959     },
41960
41961     /**
41962      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
41963      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
41964      */
41965     //setCollapsedTitle : function(title){
41966     //    title = title || "&#160;";
41967      //   if(this.collapsedTitleTextEl){
41968       //      this.collapsedTitleTextEl.innerHTML = title;
41969        // }
41970     //},
41971
41972     getBox : function(){
41973         var b;
41974       //  if(!this.collapsed){
41975             b = this.el.getBox(false, true);
41976        // }else{
41977           //  b = this.collapsedEl.getBox(false, true);
41978         //}
41979         return b;
41980     },
41981
41982     getMargins : function(){
41983         return this.margins;
41984         //return this.collapsed ? this.cmargins : this.margins;
41985     },
41986 /*
41987     highlight : function(){
41988         this.el.addClass("x-layout-panel-dragover");
41989     },
41990
41991     unhighlight : function(){
41992         this.el.removeClass("x-layout-panel-dragover");
41993     },
41994 */
41995     updateBox : function(box)
41996     {
41997         if (!this.bodyEl) {
41998             return; // not rendered yet..
41999         }
42000         
42001         this.box = box;
42002         if(!this.collapsed){
42003             this.el.dom.style.left = box.x + "px";
42004             this.el.dom.style.top = box.y + "px";
42005             this.updateBody(box.width, box.height);
42006         }else{
42007             this.collapsedEl.dom.style.left = box.x + "px";
42008             this.collapsedEl.dom.style.top = box.y + "px";
42009             this.collapsedEl.setSize(box.width, box.height);
42010         }
42011         if(this.tabs){
42012             this.tabs.autoSizeTabs();
42013         }
42014     },
42015
42016     updateBody : function(w, h)
42017     {
42018         if(w !== null){
42019             this.el.setWidth(w);
42020             w -= this.el.getBorderWidth("rl");
42021             if(this.config.adjustments){
42022                 w += this.config.adjustments[0];
42023             }
42024         }
42025         if(h !== null && h > 0){
42026             this.el.setHeight(h);
42027             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
42028             h -= this.el.getBorderWidth("tb");
42029             if(this.config.adjustments){
42030                 h += this.config.adjustments[1];
42031             }
42032             this.bodyEl.setHeight(h);
42033             if(this.tabs){
42034                 h = this.tabs.syncHeight(h);
42035             }
42036         }
42037         if(this.panelSize){
42038             w = w !== null ? w : this.panelSize.width;
42039             h = h !== null ? h : this.panelSize.height;
42040         }
42041         if(this.activePanel){
42042             var el = this.activePanel.getEl();
42043             w = w !== null ? w : el.getWidth();
42044             h = h !== null ? h : el.getHeight();
42045             this.panelSize = {width: w, height: h};
42046             this.activePanel.setSize(w, h);
42047         }
42048         if(Roo.isIE && this.tabs){
42049             this.tabs.el.repaint();
42050         }
42051     },
42052
42053     /**
42054      * Returns the container element for this region.
42055      * @return {Roo.Element}
42056      */
42057     getEl : function(){
42058         return this.el;
42059     },
42060
42061     /**
42062      * Hides this region.
42063      */
42064     hide : function(){
42065         //if(!this.collapsed){
42066             this.el.dom.style.left = "-2000px";
42067             this.el.hide();
42068         //}else{
42069          //   this.collapsedEl.dom.style.left = "-2000px";
42070          //   this.collapsedEl.hide();
42071        // }
42072         this.visible = false;
42073         this.fireEvent("visibilitychange", this, false);
42074     },
42075
42076     /**
42077      * Shows this region if it was previously hidden.
42078      */
42079     show : function(){
42080         //if(!this.collapsed){
42081             this.el.show();
42082         //}else{
42083         //    this.collapsedEl.show();
42084        // }
42085         this.visible = true;
42086         this.fireEvent("visibilitychange", this, true);
42087     },
42088 /*
42089     closeClicked : function(){
42090         if(this.activePanel){
42091             this.remove(this.activePanel);
42092         }
42093     },
42094
42095     collapseClick : function(e){
42096         if(this.isSlid){
42097            e.stopPropagation();
42098            this.slideIn();
42099         }else{
42100            e.stopPropagation();
42101            this.slideOut();
42102         }
42103     },
42104 */
42105     /**
42106      * Collapses this region.
42107      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
42108      */
42109     /*
42110     collapse : function(skipAnim, skipCheck = false){
42111         if(this.collapsed) {
42112             return;
42113         }
42114         
42115         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
42116             
42117             this.collapsed = true;
42118             if(this.split){
42119                 this.split.el.hide();
42120             }
42121             if(this.config.animate && skipAnim !== true){
42122                 this.fireEvent("invalidated", this);
42123                 this.animateCollapse();
42124             }else{
42125                 this.el.setLocation(-20000,-20000);
42126                 this.el.hide();
42127                 this.collapsedEl.show();
42128                 this.fireEvent("collapsed", this);
42129                 this.fireEvent("invalidated", this);
42130             }
42131         }
42132         
42133     },
42134 */
42135     animateCollapse : function(){
42136         // overridden
42137     },
42138
42139     /**
42140      * Expands this region if it was previously collapsed.
42141      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
42142      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
42143      */
42144     /*
42145     expand : function(e, skipAnim){
42146         if(e) {
42147             e.stopPropagation();
42148         }
42149         if(!this.collapsed || this.el.hasActiveFx()) {
42150             return;
42151         }
42152         if(this.isSlid){
42153             this.afterSlideIn();
42154             skipAnim = true;
42155         }
42156         this.collapsed = false;
42157         if(this.config.animate && skipAnim !== true){
42158             this.animateExpand();
42159         }else{
42160             this.el.show();
42161             if(this.split){
42162                 this.split.el.show();
42163             }
42164             this.collapsedEl.setLocation(-2000,-2000);
42165             this.collapsedEl.hide();
42166             this.fireEvent("invalidated", this);
42167             this.fireEvent("expanded", this);
42168         }
42169     },
42170 */
42171     animateExpand : function(){
42172         // overridden
42173     },
42174
42175     initTabs : function()
42176     {
42177         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
42178         
42179         var ts = new Roo.bootstrap.panel.Tabs({
42180             el: this.bodyEl.dom,
42181             region : this,
42182             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
42183             disableTooltips: this.config.disableTabTips,
42184             toolbar : this.config.toolbar
42185         });
42186         
42187         if(this.config.hideTabs){
42188             ts.stripWrap.setDisplayed(false);
42189         }
42190         this.tabs = ts;
42191         ts.resizeTabs = this.config.resizeTabs === true;
42192         ts.minTabWidth = this.config.minTabWidth || 40;
42193         ts.maxTabWidth = this.config.maxTabWidth || 250;
42194         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
42195         ts.monitorResize = false;
42196         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
42197         ts.bodyEl.addClass('roo-layout-tabs-body');
42198         this.panels.each(this.initPanelAsTab, this);
42199     },
42200
42201     initPanelAsTab : function(panel){
42202         var ti = this.tabs.addTab(
42203             panel.getEl().id,
42204             panel.getTitle(),
42205             null,
42206             this.config.closeOnTab && panel.isClosable(),
42207             panel.tpl
42208         );
42209         if(panel.tabTip !== undefined){
42210             ti.setTooltip(panel.tabTip);
42211         }
42212         ti.on("activate", function(){
42213               this.setActivePanel(panel);
42214         }, this);
42215         
42216         if(this.config.closeOnTab){
42217             ti.on("beforeclose", function(t, e){
42218                 e.cancel = true;
42219                 this.remove(panel);
42220             }, this);
42221         }
42222         
42223         panel.tabItem = ti;
42224         
42225         return ti;
42226     },
42227
42228     updatePanelTitle : function(panel, title)
42229     {
42230         if(this.activePanel == panel){
42231             this.updateTitle(title);
42232         }
42233         if(this.tabs){
42234             var ti = this.tabs.getTab(panel.getEl().id);
42235             ti.setText(title);
42236             if(panel.tabTip !== undefined){
42237                 ti.setTooltip(panel.tabTip);
42238             }
42239         }
42240     },
42241
42242     updateTitle : function(title){
42243         if(this.titleTextEl && !this.config.title){
42244             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
42245         }
42246     },
42247
42248     setActivePanel : function(panel)
42249     {
42250         panel = this.getPanel(panel);
42251         if(this.activePanel && this.activePanel != panel){
42252             if(this.activePanel.setActiveState(false) === false){
42253                 return;
42254             }
42255         }
42256         this.activePanel = panel;
42257         panel.setActiveState(true);
42258         if(this.panelSize){
42259             panel.setSize(this.panelSize.width, this.panelSize.height);
42260         }
42261         if(this.closeBtn){
42262             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
42263         }
42264         this.updateTitle(panel.getTitle());
42265         if(this.tabs){
42266             this.fireEvent("invalidated", this);
42267         }
42268         this.fireEvent("panelactivated", this, panel);
42269     },
42270
42271     /**
42272      * Shows the specified panel.
42273      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
42274      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
42275      */
42276     showPanel : function(panel)
42277     {
42278         panel = this.getPanel(panel);
42279         if(panel){
42280             if(this.tabs){
42281                 var tab = this.tabs.getTab(panel.getEl().id);
42282                 if(tab.isHidden()){
42283                     this.tabs.unhideTab(tab.id);
42284                 }
42285                 tab.activate();
42286             }else{
42287                 this.setActivePanel(panel);
42288             }
42289         }
42290         return panel;
42291     },
42292
42293     /**
42294      * Get the active panel for this region.
42295      * @return {Roo.ContentPanel} The active panel or null
42296      */
42297     getActivePanel : function(){
42298         return this.activePanel;
42299     },
42300
42301     validateVisibility : function(){
42302         if(this.panels.getCount() < 1){
42303             this.updateTitle("&#160;");
42304             this.closeBtn.hide();
42305             this.hide();
42306         }else{
42307             if(!this.isVisible()){
42308                 this.show();
42309             }
42310         }
42311     },
42312
42313     /**
42314      * Adds the passed ContentPanel(s) to this region.
42315      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42316      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
42317      */
42318     add : function(panel)
42319     {
42320         if(arguments.length > 1){
42321             for(var i = 0, len = arguments.length; i < len; i++) {
42322                 this.add(arguments[i]);
42323             }
42324             return null;
42325         }
42326         
42327         // if we have not been rendered yet, then we can not really do much of this..
42328         if (!this.bodyEl) {
42329             this.unrendered_panels.push(panel);
42330             return panel;
42331         }
42332         
42333         
42334         
42335         
42336         if(this.hasPanel(panel)){
42337             this.showPanel(panel);
42338             return panel;
42339         }
42340         panel.setRegion(this);
42341         this.panels.add(panel);
42342        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
42343             // sinle panel - no tab...?? would it not be better to render it with the tabs,
42344             // and hide them... ???
42345             this.bodyEl.dom.appendChild(panel.getEl().dom);
42346             if(panel.background !== true){
42347                 this.setActivePanel(panel);
42348             }
42349             this.fireEvent("paneladded", this, panel);
42350             return panel;
42351         }
42352         */
42353         if(!this.tabs){
42354             this.initTabs();
42355         }else{
42356             this.initPanelAsTab(panel);
42357         }
42358         
42359         
42360         if(panel.background !== true){
42361             this.tabs.activate(panel.getEl().id);
42362         }
42363         this.fireEvent("paneladded", this, panel);
42364         return panel;
42365     },
42366
42367     /**
42368      * Hides the tab for the specified panel.
42369      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42370      */
42371     hidePanel : function(panel){
42372         if(this.tabs && (panel = this.getPanel(panel))){
42373             this.tabs.hideTab(panel.getEl().id);
42374         }
42375     },
42376
42377     /**
42378      * Unhides the tab for a previously hidden panel.
42379      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42380      */
42381     unhidePanel : function(panel){
42382         if(this.tabs && (panel = this.getPanel(panel))){
42383             this.tabs.unhideTab(panel.getEl().id);
42384         }
42385     },
42386
42387     clearPanels : function(){
42388         while(this.panels.getCount() > 0){
42389              this.remove(this.panels.first());
42390         }
42391     },
42392
42393     /**
42394      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42395      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42396      * @param {Boolean} preservePanel Overrides the config preservePanel option
42397      * @return {Roo.ContentPanel} The panel that was removed
42398      */
42399     remove : function(panel, preservePanel)
42400     {
42401         panel = this.getPanel(panel);
42402         if(!panel){
42403             return null;
42404         }
42405         var e = {};
42406         this.fireEvent("beforeremove", this, panel, e);
42407         if(e.cancel === true){
42408             return null;
42409         }
42410         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
42411         var panelId = panel.getId();
42412         this.panels.removeKey(panelId);
42413         if(preservePanel){
42414             document.body.appendChild(panel.getEl().dom);
42415         }
42416         if(this.tabs){
42417             this.tabs.removeTab(panel.getEl().id);
42418         }else if (!preservePanel){
42419             this.bodyEl.dom.removeChild(panel.getEl().dom);
42420         }
42421         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
42422             var p = this.panels.first();
42423             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
42424             tempEl.appendChild(p.getEl().dom);
42425             this.bodyEl.update("");
42426             this.bodyEl.dom.appendChild(p.getEl().dom);
42427             tempEl = null;
42428             this.updateTitle(p.getTitle());
42429             this.tabs = null;
42430             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
42431             this.setActivePanel(p);
42432         }
42433         panel.setRegion(null);
42434         if(this.activePanel == panel){
42435             this.activePanel = null;
42436         }
42437         if(this.config.autoDestroy !== false && preservePanel !== true){
42438             try{panel.destroy();}catch(e){}
42439         }
42440         this.fireEvent("panelremoved", this, panel);
42441         return panel;
42442     },
42443
42444     /**
42445      * Returns the TabPanel component used by this region
42446      * @return {Roo.TabPanel}
42447      */
42448     getTabs : function(){
42449         return this.tabs;
42450     },
42451
42452     createTool : function(parentEl, className){
42453         var btn = Roo.DomHelper.append(parentEl, {
42454             tag: "div",
42455             cls: "x-layout-tools-button",
42456             children: [ {
42457                 tag: "div",
42458                 cls: "roo-layout-tools-button-inner " + className,
42459                 html: "&#160;"
42460             }]
42461         }, true);
42462         btn.addClassOnOver("roo-layout-tools-button-over");
42463         return btn;
42464     }
42465 });/*
42466  * Based on:
42467  * Ext JS Library 1.1.1
42468  * Copyright(c) 2006-2007, Ext JS, LLC.
42469  *
42470  * Originally Released Under LGPL - original licence link has changed is not relivant.
42471  *
42472  * Fork - LGPL
42473  * <script type="text/javascript">
42474  */
42475  
42476
42477
42478 /**
42479  * @class Roo.SplitLayoutRegion
42480  * @extends Roo.LayoutRegion
42481  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
42482  */
42483 Roo.bootstrap.layout.Split = function(config){
42484     this.cursor = config.cursor;
42485     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
42486 };
42487
42488 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
42489 {
42490     splitTip : "Drag to resize.",
42491     collapsibleSplitTip : "Drag to resize. Double click to hide.",
42492     useSplitTips : false,
42493
42494     applyConfig : function(config){
42495         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
42496     },
42497     
42498     onRender : function(ctr,pos) {
42499         
42500         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
42501         if(!this.config.split){
42502             return;
42503         }
42504         if(!this.split){
42505             
42506             var splitEl = Roo.DomHelper.append(ctr.dom,  {
42507                             tag: "div",
42508                             id: this.el.id + "-split",
42509                             cls: "roo-layout-split roo-layout-split-"+this.position,
42510                             html: "&#160;"
42511             });
42512             /** The SplitBar for this region 
42513             * @type Roo.SplitBar */
42514             // does not exist yet...
42515             Roo.log([this.position, this.orientation]);
42516             
42517             this.split = new Roo.bootstrap.SplitBar({
42518                 dragElement : splitEl,
42519                 resizingElement: this.el,
42520                 orientation : this.orientation
42521             });
42522             
42523             this.split.on("moved", this.onSplitMove, this);
42524             this.split.useShim = this.config.useShim === true;
42525             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
42526             if(this.useSplitTips){
42527                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
42528             }
42529             //if(config.collapsible){
42530             //    this.split.el.on("dblclick", this.collapse,  this);
42531             //}
42532         }
42533         if(typeof this.config.minSize != "undefined"){
42534             this.split.minSize = this.config.minSize;
42535         }
42536         if(typeof this.config.maxSize != "undefined"){
42537             this.split.maxSize = this.config.maxSize;
42538         }
42539         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
42540             this.hideSplitter();
42541         }
42542         
42543     },
42544
42545     getHMaxSize : function(){
42546          var cmax = this.config.maxSize || 10000;
42547          var center = this.mgr.getRegion("center");
42548          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
42549     },
42550
42551     getVMaxSize : function(){
42552          var cmax = this.config.maxSize || 10000;
42553          var center = this.mgr.getRegion("center");
42554          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
42555     },
42556
42557     onSplitMove : function(split, newSize){
42558         this.fireEvent("resized", this, newSize);
42559     },
42560     
42561     /** 
42562      * Returns the {@link Roo.SplitBar} for this region.
42563      * @return {Roo.SplitBar}
42564      */
42565     getSplitBar : function(){
42566         return this.split;
42567     },
42568     
42569     hide : function(){
42570         this.hideSplitter();
42571         Roo.bootstrap.layout.Split.superclass.hide.call(this);
42572     },
42573
42574     hideSplitter : function(){
42575         if(this.split){
42576             this.split.el.setLocation(-2000,-2000);
42577             this.split.el.hide();
42578         }
42579     },
42580
42581     show : function(){
42582         if(this.split){
42583             this.split.el.show();
42584         }
42585         Roo.bootstrap.layout.Split.superclass.show.call(this);
42586     },
42587     
42588     beforeSlide: function(){
42589         if(Roo.isGecko){// firefox overflow auto bug workaround
42590             this.bodyEl.clip();
42591             if(this.tabs) {
42592                 this.tabs.bodyEl.clip();
42593             }
42594             if(this.activePanel){
42595                 this.activePanel.getEl().clip();
42596                 
42597                 if(this.activePanel.beforeSlide){
42598                     this.activePanel.beforeSlide();
42599                 }
42600             }
42601         }
42602     },
42603     
42604     afterSlide : function(){
42605         if(Roo.isGecko){// firefox overflow auto bug workaround
42606             this.bodyEl.unclip();
42607             if(this.tabs) {
42608                 this.tabs.bodyEl.unclip();
42609             }
42610             if(this.activePanel){
42611                 this.activePanel.getEl().unclip();
42612                 if(this.activePanel.afterSlide){
42613                     this.activePanel.afterSlide();
42614                 }
42615             }
42616         }
42617     },
42618
42619     initAutoHide : function(){
42620         if(this.autoHide !== false){
42621             if(!this.autoHideHd){
42622                 var st = new Roo.util.DelayedTask(this.slideIn, this);
42623                 this.autoHideHd = {
42624                     "mouseout": function(e){
42625                         if(!e.within(this.el, true)){
42626                             st.delay(500);
42627                         }
42628                     },
42629                     "mouseover" : function(e){
42630                         st.cancel();
42631                     },
42632                     scope : this
42633                 };
42634             }
42635             this.el.on(this.autoHideHd);
42636         }
42637     },
42638
42639     clearAutoHide : function(){
42640         if(this.autoHide !== false){
42641             this.el.un("mouseout", this.autoHideHd.mouseout);
42642             this.el.un("mouseover", this.autoHideHd.mouseover);
42643         }
42644     },
42645
42646     clearMonitor : function(){
42647         Roo.get(document).un("click", this.slideInIf, this);
42648     },
42649
42650     // these names are backwards but not changed for compat
42651     slideOut : function(){
42652         if(this.isSlid || this.el.hasActiveFx()){
42653             return;
42654         }
42655         this.isSlid = true;
42656         if(this.collapseBtn){
42657             this.collapseBtn.hide();
42658         }
42659         this.closeBtnState = this.closeBtn.getStyle('display');
42660         this.closeBtn.hide();
42661         if(this.stickBtn){
42662             this.stickBtn.show();
42663         }
42664         this.el.show();
42665         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
42666         this.beforeSlide();
42667         this.el.setStyle("z-index", 10001);
42668         this.el.slideIn(this.getSlideAnchor(), {
42669             callback: function(){
42670                 this.afterSlide();
42671                 this.initAutoHide();
42672                 Roo.get(document).on("click", this.slideInIf, this);
42673                 this.fireEvent("slideshow", this);
42674             },
42675             scope: this,
42676             block: true
42677         });
42678     },
42679
42680     afterSlideIn : function(){
42681         this.clearAutoHide();
42682         this.isSlid = false;
42683         this.clearMonitor();
42684         this.el.setStyle("z-index", "");
42685         if(this.collapseBtn){
42686             this.collapseBtn.show();
42687         }
42688         this.closeBtn.setStyle('display', this.closeBtnState);
42689         if(this.stickBtn){
42690             this.stickBtn.hide();
42691         }
42692         this.fireEvent("slidehide", this);
42693     },
42694
42695     slideIn : function(cb){
42696         if(!this.isSlid || this.el.hasActiveFx()){
42697             Roo.callback(cb);
42698             return;
42699         }
42700         this.isSlid = false;
42701         this.beforeSlide();
42702         this.el.slideOut(this.getSlideAnchor(), {
42703             callback: function(){
42704                 this.el.setLeftTop(-10000, -10000);
42705                 this.afterSlide();
42706                 this.afterSlideIn();
42707                 Roo.callback(cb);
42708             },
42709             scope: this,
42710             block: true
42711         });
42712     },
42713     
42714     slideInIf : function(e){
42715         if(!e.within(this.el)){
42716             this.slideIn();
42717         }
42718     },
42719
42720     animateCollapse : function(){
42721         this.beforeSlide();
42722         this.el.setStyle("z-index", 20000);
42723         var anchor = this.getSlideAnchor();
42724         this.el.slideOut(anchor, {
42725             callback : function(){
42726                 this.el.setStyle("z-index", "");
42727                 this.collapsedEl.slideIn(anchor, {duration:.3});
42728                 this.afterSlide();
42729                 this.el.setLocation(-10000,-10000);
42730                 this.el.hide();
42731                 this.fireEvent("collapsed", this);
42732             },
42733             scope: this,
42734             block: true
42735         });
42736     },
42737
42738     animateExpand : function(){
42739         this.beforeSlide();
42740         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
42741         this.el.setStyle("z-index", 20000);
42742         this.collapsedEl.hide({
42743             duration:.1
42744         });
42745         this.el.slideIn(this.getSlideAnchor(), {
42746             callback : function(){
42747                 this.el.setStyle("z-index", "");
42748                 this.afterSlide();
42749                 if(this.split){
42750                     this.split.el.show();
42751                 }
42752                 this.fireEvent("invalidated", this);
42753                 this.fireEvent("expanded", this);
42754             },
42755             scope: this,
42756             block: true
42757         });
42758     },
42759
42760     anchors : {
42761         "west" : "left",
42762         "east" : "right",
42763         "north" : "top",
42764         "south" : "bottom"
42765     },
42766
42767     sanchors : {
42768         "west" : "l",
42769         "east" : "r",
42770         "north" : "t",
42771         "south" : "b"
42772     },
42773
42774     canchors : {
42775         "west" : "tl-tr",
42776         "east" : "tr-tl",
42777         "north" : "tl-bl",
42778         "south" : "bl-tl"
42779     },
42780
42781     getAnchor : function(){
42782         return this.anchors[this.position];
42783     },
42784
42785     getCollapseAnchor : function(){
42786         return this.canchors[this.position];
42787     },
42788
42789     getSlideAnchor : function(){
42790         return this.sanchors[this.position];
42791     },
42792
42793     getAlignAdj : function(){
42794         var cm = this.cmargins;
42795         switch(this.position){
42796             case "west":
42797                 return [0, 0];
42798             break;
42799             case "east":
42800                 return [0, 0];
42801             break;
42802             case "north":
42803                 return [0, 0];
42804             break;
42805             case "south":
42806                 return [0, 0];
42807             break;
42808         }
42809     },
42810
42811     getExpandAdj : function(){
42812         var c = this.collapsedEl, cm = this.cmargins;
42813         switch(this.position){
42814             case "west":
42815                 return [-(cm.right+c.getWidth()+cm.left), 0];
42816             break;
42817             case "east":
42818                 return [cm.right+c.getWidth()+cm.left, 0];
42819             break;
42820             case "north":
42821                 return [0, -(cm.top+cm.bottom+c.getHeight())];
42822             break;
42823             case "south":
42824                 return [0, cm.top+cm.bottom+c.getHeight()];
42825             break;
42826         }
42827     }
42828 });/*
42829  * Based on:
42830  * Ext JS Library 1.1.1
42831  * Copyright(c) 2006-2007, Ext JS, LLC.
42832  *
42833  * Originally Released Under LGPL - original licence link has changed is not relivant.
42834  *
42835  * Fork - LGPL
42836  * <script type="text/javascript">
42837  */
42838 /*
42839  * These classes are private internal classes
42840  */
42841 Roo.bootstrap.layout.Center = function(config){
42842     config.region = "center";
42843     Roo.bootstrap.layout.Region.call(this, config);
42844     this.visible = true;
42845     this.minWidth = config.minWidth || 20;
42846     this.minHeight = config.minHeight || 20;
42847 };
42848
42849 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
42850     hide : function(){
42851         // center panel can't be hidden
42852     },
42853     
42854     show : function(){
42855         // center panel can't be hidden
42856     },
42857     
42858     getMinWidth: function(){
42859         return this.minWidth;
42860     },
42861     
42862     getMinHeight: function(){
42863         return this.minHeight;
42864     }
42865 });
42866
42867
42868
42869
42870  
42871
42872
42873
42874
42875
42876
42877 Roo.bootstrap.layout.North = function(config)
42878 {
42879     config.region = 'north';
42880     config.cursor = 'n-resize';
42881     
42882     Roo.bootstrap.layout.Split.call(this, config);
42883     
42884     
42885     if(this.split){
42886         this.split.placement = Roo.bootstrap.SplitBar.TOP;
42887         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42888         this.split.el.addClass("roo-layout-split-v");
42889     }
42890     //var size = config.initialSize || config.height;
42891     //if(this.el && typeof size != "undefined"){
42892     //    this.el.setHeight(size);
42893     //}
42894 };
42895 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
42896 {
42897     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42898      
42899      
42900     onRender : function(ctr, pos)
42901     {
42902         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42903         var size = this.config.initialSize || this.config.height;
42904         if(this.el && typeof size != "undefined"){
42905             this.el.setHeight(size);
42906         }
42907     
42908     },
42909     
42910     getBox : function(){
42911         if(this.collapsed){
42912             return this.collapsedEl.getBox();
42913         }
42914         var box = this.el.getBox();
42915         if(this.split){
42916             box.height += this.split.el.getHeight();
42917         }
42918         return box;
42919     },
42920     
42921     updateBox : function(box){
42922         if(this.split && !this.collapsed){
42923             box.height -= this.split.el.getHeight();
42924             this.split.el.setLeft(box.x);
42925             this.split.el.setTop(box.y+box.height);
42926             this.split.el.setWidth(box.width);
42927         }
42928         if(this.collapsed){
42929             this.updateBody(box.width, null);
42930         }
42931         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42932     }
42933 });
42934
42935
42936
42937
42938
42939 Roo.bootstrap.layout.South = function(config){
42940     config.region = 'south';
42941     config.cursor = 's-resize';
42942     Roo.bootstrap.layout.Split.call(this, config);
42943     if(this.split){
42944         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
42945         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42946         this.split.el.addClass("roo-layout-split-v");
42947     }
42948     
42949 };
42950
42951 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
42952     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42953     
42954     onRender : function(ctr, pos)
42955     {
42956         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42957         var size = this.config.initialSize || this.config.height;
42958         if(this.el && typeof size != "undefined"){
42959             this.el.setHeight(size);
42960         }
42961     
42962     },
42963     
42964     getBox : function(){
42965         if(this.collapsed){
42966             return this.collapsedEl.getBox();
42967         }
42968         var box = this.el.getBox();
42969         if(this.split){
42970             var sh = this.split.el.getHeight();
42971             box.height += sh;
42972             box.y -= sh;
42973         }
42974         return box;
42975     },
42976     
42977     updateBox : function(box){
42978         if(this.split && !this.collapsed){
42979             var sh = this.split.el.getHeight();
42980             box.height -= sh;
42981             box.y += sh;
42982             this.split.el.setLeft(box.x);
42983             this.split.el.setTop(box.y-sh);
42984             this.split.el.setWidth(box.width);
42985         }
42986         if(this.collapsed){
42987             this.updateBody(box.width, null);
42988         }
42989         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42990     }
42991 });
42992
42993 Roo.bootstrap.layout.East = function(config){
42994     config.region = "east";
42995     config.cursor = "e-resize";
42996     Roo.bootstrap.layout.Split.call(this, config);
42997     if(this.split){
42998         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
42999         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
43000         this.split.el.addClass("roo-layout-split-h");
43001     }
43002     
43003 };
43004 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
43005     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
43006     
43007     onRender : function(ctr, pos)
43008     {
43009         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
43010         var size = this.config.initialSize || this.config.width;
43011         if(this.el && typeof size != "undefined"){
43012             this.el.setWidth(size);
43013         }
43014     
43015     },
43016     
43017     getBox : function(){
43018         if(this.collapsed){
43019             return this.collapsedEl.getBox();
43020         }
43021         var box = this.el.getBox();
43022         if(this.split){
43023             var sw = this.split.el.getWidth();
43024             box.width += sw;
43025             box.x -= sw;
43026         }
43027         return box;
43028     },
43029
43030     updateBox : function(box){
43031         if(this.split && !this.collapsed){
43032             var sw = this.split.el.getWidth();
43033             box.width -= sw;
43034             this.split.el.setLeft(box.x);
43035             this.split.el.setTop(box.y);
43036             this.split.el.setHeight(box.height);
43037             box.x += sw;
43038         }
43039         if(this.collapsed){
43040             this.updateBody(null, box.height);
43041         }
43042         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
43043     }
43044 });
43045
43046 Roo.bootstrap.layout.West = function(config){
43047     config.region = "west";
43048     config.cursor = "w-resize";
43049     
43050     Roo.bootstrap.layout.Split.call(this, config);
43051     if(this.split){
43052         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
43053         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
43054         this.split.el.addClass("roo-layout-split-h");
43055     }
43056     
43057 };
43058 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
43059     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
43060     
43061     onRender: function(ctr, pos)
43062     {
43063         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
43064         var size = this.config.initialSize || this.config.width;
43065         if(typeof size != "undefined"){
43066             this.el.setWidth(size);
43067         }
43068     },
43069     
43070     getBox : function(){
43071         if(this.collapsed){
43072             return this.collapsedEl.getBox();
43073         }
43074         var box = this.el.getBox();
43075         if (box.width == 0) {
43076             box.width = this.config.width; // kludge?
43077         }
43078         if(this.split){
43079             box.width += this.split.el.getWidth();
43080         }
43081         return box;
43082     },
43083     
43084     updateBox : function(box){
43085         if(this.split && !this.collapsed){
43086             var sw = this.split.el.getWidth();
43087             box.width -= sw;
43088             this.split.el.setLeft(box.x+box.width);
43089             this.split.el.setTop(box.y);
43090             this.split.el.setHeight(box.height);
43091         }
43092         if(this.collapsed){
43093             this.updateBody(null, box.height);
43094         }
43095         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
43096     }
43097 });/*
43098  * Based on:
43099  * Ext JS Library 1.1.1
43100  * Copyright(c) 2006-2007, Ext JS, LLC.
43101  *
43102  * Originally Released Under LGPL - original licence link has changed is not relivant.
43103  *
43104  * Fork - LGPL
43105  * <script type="text/javascript">
43106  */
43107 /**
43108  * @class Roo.bootstrap.paenl.Content
43109  * @extends Roo.util.Observable
43110  * @children Roo.bootstrap.Component
43111  * @parent builder Roo.bootstrap.layout.Border
43112  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
43113  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
43114  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
43115  * @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
43116  * @cfg {Boolean}   closable      True if the panel can be closed/removed
43117  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
43118  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
43119  * @cfg {Toolbar}   toolbar       A toolbar for this panel
43120  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
43121  * @cfg {String} title          The title for this panel
43122  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
43123  * @cfg {String} url            Calls {@link #setUrl} with this value
43124  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
43125  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
43126  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
43127  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
43128  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
43129  * @cfg {Boolean} badges render the badges
43130  * @cfg {String} cls  extra classes to use  
43131  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
43132  
43133  * @constructor
43134  * Create a new ContentPanel.
43135  * @param {String/Object} config A string to set only the title or a config object
43136  
43137  */
43138 Roo.bootstrap.panel.Content = function( config){
43139     
43140     this.tpl = config.tpl || false;
43141     
43142     var el = config.el;
43143     var content = config.content;
43144
43145     if(config.autoCreate){ // xtype is available if this is called from factory
43146         el = Roo.id();
43147     }
43148     this.el = Roo.get(el);
43149     if(!this.el && config && config.autoCreate){
43150         if(typeof config.autoCreate == "object"){
43151             if(!config.autoCreate.id){
43152                 config.autoCreate.id = config.id||el;
43153             }
43154             this.el = Roo.DomHelper.append(document.body,
43155                         config.autoCreate, true);
43156         }else{
43157             var elcfg =  {
43158                 tag: "div",
43159                 cls: (config.cls || '') +
43160                     (config.background ? ' bg-' + config.background : '') +
43161                     " roo-layout-inactive-content",
43162                 id: config.id||el
43163             };
43164             if (config.iframe) {
43165                 elcfg.cn = [
43166                     {
43167                         tag : 'iframe',
43168                         style : 'border: 0px',
43169                         src : 'about:blank'
43170                     }
43171                 ];
43172             }
43173               
43174             if (config.html) {
43175                 elcfg.html = config.html;
43176                 
43177             }
43178                         
43179             this.el = Roo.DomHelper.append(document.body, elcfg , true);
43180             if (config.iframe) {
43181                 this.iframeEl = this.el.select('iframe',true).first();
43182             }
43183             
43184         }
43185     } 
43186     this.closable = false;
43187     this.loaded = false;
43188     this.active = false;
43189    
43190       
43191     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
43192         
43193         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
43194         
43195         this.wrapEl = this.el; //this.el.wrap();
43196         var ti = [];
43197         if (config.toolbar.items) {
43198             ti = config.toolbar.items ;
43199             delete config.toolbar.items ;
43200         }
43201         
43202         var nitems = [];
43203         this.toolbar.render(this.wrapEl, 'before');
43204         for(var i =0;i < ti.length;i++) {
43205           //  Roo.log(['add child', items[i]]);
43206             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43207         }
43208         this.toolbar.items = nitems;
43209         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
43210         delete config.toolbar;
43211         
43212     }
43213     /*
43214     // xtype created footer. - not sure if will work as we normally have to render first..
43215     if (this.footer && !this.footer.el && this.footer.xtype) {
43216         if (!this.wrapEl) {
43217             this.wrapEl = this.el.wrap();
43218         }
43219     
43220         this.footer.container = this.wrapEl.createChild();
43221          
43222         this.footer = Roo.factory(this.footer, Roo);
43223         
43224     }
43225     */
43226     
43227      if(typeof config == "string"){
43228         this.title = config;
43229     }else{
43230         Roo.apply(this, config);
43231     }
43232     
43233     if(this.resizeEl){
43234         this.resizeEl = Roo.get(this.resizeEl, true);
43235     }else{
43236         this.resizeEl = this.el;
43237     }
43238     // handle view.xtype
43239     
43240  
43241     
43242     
43243     this.addEvents({
43244         /**
43245          * @event activate
43246          * Fires when this panel is activated. 
43247          * @param {Roo.ContentPanel} this
43248          */
43249         "activate" : true,
43250         /**
43251          * @event deactivate
43252          * Fires when this panel is activated. 
43253          * @param {Roo.ContentPanel} this
43254          */
43255         "deactivate" : true,
43256
43257         /**
43258          * @event resize
43259          * Fires when this panel is resized if fitToFrame is true.
43260          * @param {Roo.ContentPanel} this
43261          * @param {Number} width The width after any component adjustments
43262          * @param {Number} height The height after any component adjustments
43263          */
43264         "resize" : true,
43265         
43266          /**
43267          * @event render
43268          * Fires when this tab is created
43269          * @param {Roo.ContentPanel} this
43270          */
43271         "render" : true,
43272         
43273           /**
43274          * @event scroll
43275          * Fires when this content is scrolled
43276          * @param {Roo.ContentPanel} this
43277          * @param {Event} scrollEvent
43278          */
43279         "scroll" : true
43280         
43281         
43282         
43283     });
43284     
43285
43286     
43287     
43288     if(this.autoScroll && !this.iframe){
43289         this.resizeEl.setStyle("overflow", "auto");
43290         this.resizeEl.on('scroll', this.onScroll, this);
43291     } else {
43292         // fix randome scrolling
43293         //this.el.on('scroll', function() {
43294         //    Roo.log('fix random scolling');
43295         //    this.scrollTo('top',0); 
43296         //});
43297     }
43298     content = content || this.content;
43299     if(content){
43300         this.setContent(content);
43301     }
43302     if(config && config.url){
43303         this.setUrl(this.url, this.params, this.loadOnce);
43304     }
43305     
43306     
43307     
43308     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
43309     
43310     if (this.view && typeof(this.view.xtype) != 'undefined') {
43311         this.view.el = this.el.appendChild(document.createElement("div"));
43312         this.view = Roo.factory(this.view); 
43313         this.view.render  &&  this.view.render(false, '');  
43314     }
43315     
43316     
43317     this.fireEvent('render', this);
43318 };
43319
43320 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
43321     
43322     cls : '',
43323     background : '',
43324     
43325     tabTip : '',
43326     
43327     iframe : false,
43328     iframeEl : false,
43329     
43330     /* Resize Element - use this to work out scroll etc. */
43331     resizeEl : false,
43332     
43333     setRegion : function(region){
43334         this.region = region;
43335         this.setActiveClass(region && !this.background);
43336     },
43337     
43338     
43339     setActiveClass: function(state)
43340     {
43341         if(state){
43342            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
43343            this.el.setStyle('position','relative');
43344         }else{
43345            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
43346            this.el.setStyle('position', 'absolute');
43347         } 
43348     },
43349     
43350     /**
43351      * Returns the toolbar for this Panel if one was configured. 
43352      * @return {Roo.Toolbar} 
43353      */
43354     getToolbar : function(){
43355         return this.toolbar;
43356     },
43357     
43358     setActiveState : function(active)
43359     {
43360         this.active = active;
43361         this.setActiveClass(active);
43362         if(!active){
43363             if(this.fireEvent("deactivate", this) === false){
43364                 return false;
43365             }
43366             return true;
43367         }
43368         this.fireEvent("activate", this);
43369         return true;
43370     },
43371     /**
43372      * Updates this panel's element (not for iframe)
43373      * @param {String} content The new content
43374      * @param {Boolean} loadScripts (optional) true to look for and process scripts
43375     */
43376     setContent : function(content, loadScripts){
43377         if (this.iframe) {
43378             return;
43379         }
43380         
43381         this.el.update(content, loadScripts);
43382     },
43383
43384     ignoreResize : function(w, h)
43385     {
43386         //return false; // always resize?
43387         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
43388             return true;
43389         }else{
43390             this.lastSize = {width: w, height: h};
43391             return false;
43392         }
43393     },
43394     /**
43395      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
43396      * @return {Roo.UpdateManager} The UpdateManager
43397      */
43398     getUpdateManager : function(){
43399         if (this.iframe) {
43400             return false;
43401         }
43402         return this.el.getUpdateManager();
43403     },
43404      /**
43405      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
43406      * Does not work with IFRAME contents
43407      * @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:
43408 <pre><code>
43409 panel.load({
43410     url: "your-url.php",
43411     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
43412     callback: yourFunction,
43413     scope: yourObject, //(optional scope)
43414     discardUrl: false,
43415     nocache: false,
43416     text: "Loading...",
43417     timeout: 30,
43418     scripts: false
43419 });
43420 </code></pre>
43421      
43422      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
43423      * 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.
43424      * @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}
43425      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
43426      * @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.
43427      * @return {Roo.ContentPanel} this
43428      */
43429     load : function(){
43430         
43431         if (this.iframe) {
43432             return this;
43433         }
43434         
43435         var um = this.el.getUpdateManager();
43436         um.update.apply(um, arguments);
43437         return this;
43438     },
43439
43440
43441     /**
43442      * 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.
43443      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
43444      * @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)
43445      * @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)
43446      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
43447      */
43448     setUrl : function(url, params, loadOnce){
43449         if (this.iframe) {
43450             this.iframeEl.dom.src = url;
43451             return false;
43452         }
43453         
43454         if(this.refreshDelegate){
43455             this.removeListener("activate", this.refreshDelegate);
43456         }
43457         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
43458         this.on("activate", this.refreshDelegate);
43459         return this.el.getUpdateManager();
43460     },
43461     
43462     _handleRefresh : function(url, params, loadOnce){
43463         if(!loadOnce || !this.loaded){
43464             var updater = this.el.getUpdateManager();
43465             updater.update(url, params, this._setLoaded.createDelegate(this));
43466         }
43467     },
43468     
43469     _setLoaded : function(){
43470         this.loaded = true;
43471     }, 
43472     
43473     /**
43474      * Returns this panel's id
43475      * @return {String} 
43476      */
43477     getId : function(){
43478         return this.el.id;
43479     },
43480     
43481     /** 
43482      * Returns this panel's element - used by regiosn to add.
43483      * @return {Roo.Element} 
43484      */
43485     getEl : function(){
43486         return this.wrapEl || this.el;
43487     },
43488     
43489    
43490     
43491     adjustForComponents : function(width, height)
43492     {
43493         //Roo.log('adjustForComponents ');
43494         if(this.resizeEl != this.el){
43495             width -= this.el.getFrameWidth('lr');
43496             height -= this.el.getFrameWidth('tb');
43497         }
43498         if(this.toolbar){
43499             var te = this.toolbar.getEl();
43500             te.setWidth(width);
43501             height -= te.getHeight();
43502         }
43503         if(this.footer){
43504             var te = this.footer.getEl();
43505             te.setWidth(width);
43506             height -= te.getHeight();
43507         }
43508         
43509         
43510         if(this.adjustments){
43511             width += this.adjustments[0];
43512             height += this.adjustments[1];
43513         }
43514         return {"width": width, "height": height};
43515     },
43516     
43517     setSize : function(width, height){
43518         if(this.fitToFrame && !this.ignoreResize(width, height)){
43519             if(this.fitContainer && this.resizeEl != this.el){
43520                 this.el.setSize(width, height);
43521             }
43522             var size = this.adjustForComponents(width, height);
43523             if (this.iframe) {
43524                 this.iframeEl.setSize(width,height);
43525             }
43526             
43527             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
43528             this.fireEvent('resize', this, size.width, size.height);
43529             
43530             
43531         }
43532     },
43533     
43534     /**
43535      * Returns this panel's title
43536      * @return {String} 
43537      */
43538     getTitle : function(){
43539         
43540         if (typeof(this.title) != 'object') {
43541             return this.title;
43542         }
43543         
43544         var t = '';
43545         for (var k in this.title) {
43546             if (!this.title.hasOwnProperty(k)) {
43547                 continue;
43548             }
43549             
43550             if (k.indexOf('-') >= 0) {
43551                 var s = k.split('-');
43552                 for (var i = 0; i<s.length; i++) {
43553                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
43554                 }
43555             } else {
43556                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
43557             }
43558         }
43559         return t;
43560     },
43561     
43562     /**
43563      * Set this panel's title
43564      * @param {String} title
43565      */
43566     setTitle : function(title){
43567         this.title = title;
43568         if(this.region){
43569             this.region.updatePanelTitle(this, title);
43570         }
43571     },
43572     
43573     /**
43574      * Returns true is this panel was configured to be closable
43575      * @return {Boolean} 
43576      */
43577     isClosable : function(){
43578         return this.closable;
43579     },
43580     
43581     beforeSlide : function(){
43582         this.el.clip();
43583         this.resizeEl.clip();
43584     },
43585     
43586     afterSlide : function(){
43587         this.el.unclip();
43588         this.resizeEl.unclip();
43589     },
43590     
43591     /**
43592      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
43593      *   Will fail silently if the {@link #setUrl} method has not been called.
43594      *   This does not activate the panel, just updates its content.
43595      */
43596     refresh : function(){
43597         if(this.refreshDelegate){
43598            this.loaded = false;
43599            this.refreshDelegate();
43600         }
43601     },
43602     
43603     /**
43604      * Destroys this panel
43605      */
43606     destroy : function(){
43607         this.el.removeAllListeners();
43608         var tempEl = document.createElement("span");
43609         tempEl.appendChild(this.el.dom);
43610         tempEl.innerHTML = "";
43611         this.el.remove();
43612         this.el = null;
43613     },
43614     
43615     /**
43616      * form - if the content panel contains a form - this is a reference to it.
43617      * @type {Roo.form.Form}
43618      */
43619     form : false,
43620     /**
43621      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
43622      *    This contains a reference to it.
43623      * @type {Roo.View}
43624      */
43625     view : false,
43626     
43627       /**
43628      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
43629      * <pre><code>
43630
43631 layout.addxtype({
43632        xtype : 'Form',
43633        items: [ .... ]
43634    }
43635 );
43636
43637 </code></pre>
43638      * @param {Object} cfg Xtype definition of item to add.
43639      */
43640     
43641     
43642     getChildContainer: function () {
43643         return this.getEl();
43644     },
43645     
43646     
43647     onScroll : function(e)
43648     {
43649         this.fireEvent('scroll', this, e);
43650     }
43651     
43652     
43653     /*
43654         var  ret = new Roo.factory(cfg);
43655         return ret;
43656         
43657         
43658         // add form..
43659         if (cfg.xtype.match(/^Form$/)) {
43660             
43661             var el;
43662             //if (this.footer) {
43663             //    el = this.footer.container.insertSibling(false, 'before');
43664             //} else {
43665                 el = this.el.createChild();
43666             //}
43667
43668             this.form = new  Roo.form.Form(cfg);
43669             
43670             
43671             if ( this.form.allItems.length) {
43672                 this.form.render(el.dom);
43673             }
43674             return this.form;
43675         }
43676         // should only have one of theses..
43677         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
43678             // views.. should not be just added - used named prop 'view''
43679             
43680             cfg.el = this.el.appendChild(document.createElement("div"));
43681             // factory?
43682             
43683             var ret = new Roo.factory(cfg);
43684              
43685              ret.render && ret.render(false, ''); // render blank..
43686             this.view = ret;
43687             return ret;
43688         }
43689         return false;
43690     }
43691     \*/
43692 });
43693  
43694 /**
43695  * @class Roo.bootstrap.panel.Grid
43696  * @extends Roo.bootstrap.panel.Content
43697  * @constructor
43698  * Create a new GridPanel.
43699  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
43700  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
43701  * @param {Object} config A the config object
43702   
43703  */
43704
43705
43706
43707 Roo.bootstrap.panel.Grid = function(config)
43708 {
43709     
43710       
43711     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
43712         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
43713
43714     config.el = this.wrapper;
43715     //this.el = this.wrapper;
43716     
43717       if (config.container) {
43718         // ctor'ed from a Border/panel.grid
43719         
43720         
43721         this.wrapper.setStyle("overflow", "hidden");
43722         this.wrapper.addClass('roo-grid-container');
43723
43724     }
43725     
43726     
43727     if(config.toolbar){
43728         var tool_el = this.wrapper.createChild();    
43729         this.toolbar = Roo.factory(config.toolbar);
43730         var ti = [];
43731         if (config.toolbar.items) {
43732             ti = config.toolbar.items ;
43733             delete config.toolbar.items ;
43734         }
43735         
43736         var nitems = [];
43737         this.toolbar.render(tool_el);
43738         for(var i =0;i < ti.length;i++) {
43739           //  Roo.log(['add child', items[i]]);
43740             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43741         }
43742         this.toolbar.items = nitems;
43743         
43744         delete config.toolbar;
43745     }
43746     
43747     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
43748     config.grid.scrollBody = true;;
43749     config.grid.monitorWindowResize = false; // turn off autosizing
43750     config.grid.autoHeight = false;
43751     config.grid.autoWidth = false;
43752     
43753     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
43754     
43755     if (config.background) {
43756         // render grid on panel activation (if panel background)
43757         this.on('activate', function(gp) {
43758             if (!gp.grid.rendered) {
43759                 gp.grid.render(this.wrapper);
43760                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
43761             }
43762         });
43763             
43764     } else {
43765         this.grid.render(this.wrapper);
43766         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
43767
43768     }
43769     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
43770     // ??? needed ??? config.el = this.wrapper;
43771     
43772     
43773     
43774   
43775     // xtype created footer. - not sure if will work as we normally have to render first..
43776     if (this.footer && !this.footer.el && this.footer.xtype) {
43777         
43778         var ctr = this.grid.getView().getFooterPanel(true);
43779         this.footer.dataSource = this.grid.dataSource;
43780         this.footer = Roo.factory(this.footer, Roo);
43781         this.footer.render(ctr);
43782         
43783     }
43784     
43785     
43786     
43787     
43788      
43789 };
43790
43791 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
43792 {
43793   
43794     getId : function(){
43795         return this.grid.id;
43796     },
43797     
43798     /**
43799      * Returns the grid for this panel
43800      * @return {Roo.bootstrap.Table} 
43801      */
43802     getGrid : function(){
43803         return this.grid;    
43804     },
43805     
43806     setSize : function(width, height)
43807     {
43808      
43809         //if(!this.ignoreResize(width, height)){
43810             var grid = this.grid;
43811             var size = this.adjustForComponents(width, height);
43812             // tfoot is not a footer?
43813           
43814             
43815             var gridel = grid.getGridEl();
43816             gridel.setSize(size.width, size.height);
43817             
43818             var tbd = grid.getGridEl().select('tbody', true).first();
43819             var thd = grid.getGridEl().select('thead',true).first();
43820             var tbf= grid.getGridEl().select('tfoot', true).first();
43821
43822             if (tbf) {
43823                 size.height -= tbf.getHeight();
43824             }
43825             if (thd) {
43826                 size.height -= thd.getHeight();
43827             }
43828             
43829             tbd.setSize(size.width, size.height );
43830             // this is for the account management tab -seems to work there.
43831             var thd = grid.getGridEl().select('thead',true).first();
43832             //if (tbd) {
43833             //    tbd.setSize(size.width, size.height - thd.getHeight());
43834             //}
43835              
43836             grid.autoSize();
43837         //}
43838    
43839     },
43840      
43841     
43842     
43843     beforeSlide : function(){
43844         this.grid.getView().scroller.clip();
43845     },
43846     
43847     afterSlide : function(){
43848         this.grid.getView().scroller.unclip();
43849     },
43850     
43851     destroy : function(){
43852         this.grid.destroy();
43853         delete this.grid;
43854         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
43855     }
43856 });
43857
43858 /**
43859  * @class Roo.bootstrap.panel.Nest
43860  * @extends Roo.bootstrap.panel.Content
43861  * @constructor
43862  * Create a new Panel, that can contain a layout.Border.
43863  * 
43864  * 
43865  * @param {String/Object} config A string to set only the title or a config object
43866  */
43867 Roo.bootstrap.panel.Nest = function(config)
43868 {
43869     // construct with only one argument..
43870     /* FIXME - implement nicer consturctors
43871     if (layout.layout) {
43872         config = layout;
43873         layout = config.layout;
43874         delete config.layout;
43875     }
43876     if (layout.xtype && !layout.getEl) {
43877         // then layout needs constructing..
43878         layout = Roo.factory(layout, Roo);
43879     }
43880     */
43881     
43882     config.el =  config.layout.getEl();
43883     
43884     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
43885     
43886     config.layout.monitorWindowResize = false; // turn off autosizing
43887     this.layout = config.layout;
43888     this.layout.getEl().addClass("roo-layout-nested-layout");
43889     this.layout.parent = this;
43890     
43891     
43892     
43893     
43894 };
43895
43896 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
43897     /**
43898     * @cfg {Roo.BorderLayout} layout The layout for this panel
43899     */
43900     layout : false,
43901
43902     setSize : function(width, height){
43903         if(!this.ignoreResize(width, height)){
43904             var size = this.adjustForComponents(width, height);
43905             var el = this.layout.getEl();
43906             if (size.height < 1) {
43907                 el.setWidth(size.width);   
43908             } else {
43909                 el.setSize(size.width, size.height);
43910             }
43911             var touch = el.dom.offsetWidth;
43912             this.layout.layout();
43913             // ie requires a double layout on the first pass
43914             if(Roo.isIE && !this.initialized){
43915                 this.initialized = true;
43916                 this.layout.layout();
43917             }
43918         }
43919     },
43920     
43921     // activate all subpanels if not currently active..
43922     
43923     setActiveState : function(active){
43924         this.active = active;
43925         this.setActiveClass(active);
43926         
43927         if(!active){
43928             this.fireEvent("deactivate", this);
43929             return;
43930         }
43931         
43932         this.fireEvent("activate", this);
43933         // not sure if this should happen before or after..
43934         if (!this.layout) {
43935             return; // should not happen..
43936         }
43937         var reg = false;
43938         for (var r in this.layout.regions) {
43939             reg = this.layout.getRegion(r);
43940             if (reg.getActivePanel()) {
43941                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
43942                 reg.setActivePanel(reg.getActivePanel());
43943                 continue;
43944             }
43945             if (!reg.panels.length) {
43946                 continue;
43947             }
43948             reg.showPanel(reg.getPanel(0));
43949         }
43950         
43951         
43952         
43953         
43954     },
43955     
43956     /**
43957      * Returns the nested BorderLayout for this panel
43958      * @return {Roo.BorderLayout} 
43959      */
43960     getLayout : function(){
43961         return this.layout;
43962     },
43963     
43964      /**
43965      * Adds a xtype elements to the layout of the nested panel
43966      * <pre><code>
43967
43968 panel.addxtype({
43969        xtype : 'ContentPanel',
43970        region: 'west',
43971        items: [ .... ]
43972    }
43973 );
43974
43975 panel.addxtype({
43976         xtype : 'NestedLayoutPanel',
43977         region: 'west',
43978         layout: {
43979            center: { },
43980            west: { }   
43981         },
43982         items : [ ... list of content panels or nested layout panels.. ]
43983    }
43984 );
43985 </code></pre>
43986      * @param {Object} cfg Xtype definition of item to add.
43987      */
43988     addxtype : function(cfg) {
43989         return this.layout.addxtype(cfg);
43990     
43991     }
43992 });/*
43993  * Based on:
43994  * Ext JS Library 1.1.1
43995  * Copyright(c) 2006-2007, Ext JS, LLC.
43996  *
43997  * Originally Released Under LGPL - original licence link has changed is not relivant.
43998  *
43999  * Fork - LGPL
44000  * <script type="text/javascript">
44001  */
44002 /**
44003  * @class Roo.TabPanel
44004  * @extends Roo.util.Observable
44005  * A lightweight tab container.
44006  * <br><br>
44007  * Usage:
44008  * <pre><code>
44009 // basic tabs 1, built from existing content
44010 var tabs = new Roo.TabPanel("tabs1");
44011 tabs.addTab("script", "View Script");
44012 tabs.addTab("markup", "View Markup");
44013 tabs.activate("script");
44014
44015 // more advanced tabs, built from javascript
44016 var jtabs = new Roo.TabPanel("jtabs");
44017 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
44018
44019 // set up the UpdateManager
44020 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
44021 var updater = tab2.getUpdateManager();
44022 updater.setDefaultUrl("ajax1.htm");
44023 tab2.on('activate', updater.refresh, updater, true);
44024
44025 // Use setUrl for Ajax loading
44026 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
44027 tab3.setUrl("ajax2.htm", null, true);
44028
44029 // Disabled tab
44030 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
44031 tab4.disable();
44032
44033 jtabs.activate("jtabs-1");
44034  * </code></pre>
44035  * @constructor
44036  * Create a new TabPanel.
44037  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
44038  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
44039  */
44040 Roo.bootstrap.panel.Tabs = function(config){
44041     /**
44042     * The container element for this TabPanel.
44043     * @type Roo.Element
44044     */
44045     this.el = Roo.get(config.el);
44046     delete config.el;
44047     if(config){
44048         if(typeof config == "boolean"){
44049             this.tabPosition = config ? "bottom" : "top";
44050         }else{
44051             Roo.apply(this, config);
44052         }
44053     }
44054     
44055     if(this.tabPosition == "bottom"){
44056         // if tabs are at the bottom = create the body first.
44057         this.bodyEl = Roo.get(this.createBody(this.el.dom));
44058         this.el.addClass("roo-tabs-bottom");
44059     }
44060     // next create the tabs holders
44061     
44062     if (this.tabPosition == "west"){
44063         
44064         var reg = this.region; // fake it..
44065         while (reg) {
44066             if (!reg.mgr.parent) {
44067                 break;
44068             }
44069             reg = reg.mgr.parent.region;
44070         }
44071         Roo.log("got nest?");
44072         Roo.log(reg);
44073         if (reg.mgr.getRegion('west')) {
44074             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
44075             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
44076             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
44077             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
44078             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
44079         
44080             
44081         }
44082         
44083         
44084     } else {
44085      
44086         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
44087         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
44088         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
44089         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
44090     }
44091     
44092     
44093     if(Roo.isIE){
44094         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
44095     }
44096     
44097     // finally - if tabs are at the top, then create the body last..
44098     if(this.tabPosition != "bottom"){
44099         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
44100          * @type Roo.Element
44101          */
44102         this.bodyEl = Roo.get(this.createBody(this.el.dom));
44103         this.el.addClass("roo-tabs-top");
44104     }
44105     this.items = [];
44106
44107     this.bodyEl.setStyle("position", "relative");
44108
44109     this.active = null;
44110     this.activateDelegate = this.activate.createDelegate(this);
44111
44112     this.addEvents({
44113         /**
44114          * @event tabchange
44115          * Fires when the active tab changes
44116          * @param {Roo.TabPanel} this
44117          * @param {Roo.TabPanelItem} activePanel The new active tab
44118          */
44119         "tabchange": true,
44120         /**
44121          * @event beforetabchange
44122          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
44123          * @param {Roo.TabPanel} this
44124          * @param {Object} e Set cancel to true on this object to cancel the tab change
44125          * @param {Roo.TabPanelItem} tab The tab being changed to
44126          */
44127         "beforetabchange" : true
44128     });
44129
44130     Roo.EventManager.onWindowResize(this.onResize, this);
44131     this.cpad = this.el.getPadding("lr");
44132     this.hiddenCount = 0;
44133
44134
44135     // toolbar on the tabbar support...
44136     if (this.toolbar) {
44137         alert("no toolbar support yet");
44138         this.toolbar  = false;
44139         /*
44140         var tcfg = this.toolbar;
44141         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
44142         this.toolbar = new Roo.Toolbar(tcfg);
44143         if (Roo.isSafari) {
44144             var tbl = tcfg.container.child('table', true);
44145             tbl.setAttribute('width', '100%');
44146         }
44147         */
44148         
44149     }
44150    
44151
44152
44153     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
44154 };
44155
44156 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
44157     /*
44158      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
44159      */
44160     tabPosition : "top",
44161     /*
44162      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
44163      */
44164     currentTabWidth : 0,
44165     /*
44166      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
44167      */
44168     minTabWidth : 40,
44169     /*
44170      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
44171      */
44172     maxTabWidth : 250,
44173     /*
44174      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
44175      */
44176     preferredTabWidth : 175,
44177     /*
44178      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
44179      */
44180     resizeTabs : false,
44181     /*
44182      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
44183      */
44184     monitorResize : true,
44185     /*
44186      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
44187      */
44188     toolbar : false,  // set by caller..
44189     
44190     region : false, /// set by caller
44191     
44192     disableTooltips : true, // not used yet...
44193
44194     /**
44195      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
44196      * @param {String} id The id of the div to use <b>or create</b>
44197      * @param {String} text The text for the tab
44198      * @param {String} content (optional) Content to put in the TabPanelItem body
44199      * @param {Boolean} closable (optional) True to create a close icon on the tab
44200      * @return {Roo.TabPanelItem} The created TabPanelItem
44201      */
44202     addTab : function(id, text, content, closable, tpl)
44203     {
44204         var item = new Roo.bootstrap.panel.TabItem({
44205             panel: this,
44206             id : id,
44207             text : text,
44208             closable : closable,
44209             tpl : tpl
44210         });
44211         this.addTabItem(item);
44212         if(content){
44213             item.setContent(content);
44214         }
44215         return item;
44216     },
44217
44218     /**
44219      * Returns the {@link Roo.TabPanelItem} with the specified id/index
44220      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
44221      * @return {Roo.TabPanelItem}
44222      */
44223     getTab : function(id){
44224         return this.items[id];
44225     },
44226
44227     /**
44228      * Hides the {@link Roo.TabPanelItem} with the specified id/index
44229      * @param {String/Number} id The id or index of the TabPanelItem to hide.
44230      */
44231     hideTab : function(id){
44232         var t = this.items[id];
44233         if(!t.isHidden()){
44234            t.setHidden(true);
44235            this.hiddenCount++;
44236            this.autoSizeTabs();
44237         }
44238     },
44239
44240     /**
44241      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
44242      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
44243      */
44244     unhideTab : function(id){
44245         var t = this.items[id];
44246         if(t.isHidden()){
44247            t.setHidden(false);
44248            this.hiddenCount--;
44249            this.autoSizeTabs();
44250         }
44251     },
44252
44253     /**
44254      * Adds an existing {@link Roo.TabPanelItem}.
44255      * @param {Roo.TabPanelItem} item The TabPanelItem to add
44256      */
44257     addTabItem : function(item)
44258     {
44259         this.items[item.id] = item;
44260         this.items.push(item);
44261         this.autoSizeTabs();
44262       //  if(this.resizeTabs){
44263     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
44264   //         this.autoSizeTabs();
44265 //        }else{
44266 //            item.autoSize();
44267        // }
44268     },
44269
44270     /**
44271      * Removes a {@link Roo.TabPanelItem}.
44272      * @param {String/Number} id The id or index of the TabPanelItem to remove.
44273      */
44274     removeTab : function(id){
44275         var items = this.items;
44276         var tab = items[id];
44277         if(!tab) { return; }
44278         var index = items.indexOf(tab);
44279         if(this.active == tab && items.length > 1){
44280             var newTab = this.getNextAvailable(index);
44281             if(newTab) {
44282                 newTab.activate();
44283             }
44284         }
44285         this.stripEl.dom.removeChild(tab.pnode.dom);
44286         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
44287             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
44288         }
44289         items.splice(index, 1);
44290         delete this.items[tab.id];
44291         tab.fireEvent("close", tab);
44292         tab.purgeListeners();
44293         this.autoSizeTabs();
44294     },
44295
44296     getNextAvailable : function(start){
44297         var items = this.items;
44298         var index = start;
44299         // look for a next tab that will slide over to
44300         // replace the one being removed
44301         while(index < items.length){
44302             var item = items[++index];
44303             if(item && !item.isHidden()){
44304                 return item;
44305             }
44306         }
44307         // if one isn't found select the previous tab (on the left)
44308         index = start;
44309         while(index >= 0){
44310             var item = items[--index];
44311             if(item && !item.isHidden()){
44312                 return item;
44313             }
44314         }
44315         return null;
44316     },
44317
44318     /**
44319      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
44320      * @param {String/Number} id The id or index of the TabPanelItem to disable.
44321      */
44322     disableTab : function(id){
44323         var tab = this.items[id];
44324         if(tab && this.active != tab){
44325             tab.disable();
44326         }
44327     },
44328
44329     /**
44330      * Enables a {@link Roo.TabPanelItem} that is disabled.
44331      * @param {String/Number} id The id or index of the TabPanelItem to enable.
44332      */
44333     enableTab : function(id){
44334         var tab = this.items[id];
44335         tab.enable();
44336     },
44337
44338     /**
44339      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
44340      * @param {String/Number} id The id or index of the TabPanelItem to activate.
44341      * @return {Roo.TabPanelItem} The TabPanelItem.
44342      */
44343     activate : function(id)
44344     {
44345         //Roo.log('activite:'  + id);
44346         
44347         var tab = this.items[id];
44348         if(!tab){
44349             return null;
44350         }
44351         if(tab == this.active || tab.disabled){
44352             return tab;
44353         }
44354         var e = {};
44355         this.fireEvent("beforetabchange", this, e, tab);
44356         if(e.cancel !== true && !tab.disabled){
44357             if(this.active){
44358                 this.active.hide();
44359             }
44360             this.active = this.items[id];
44361             this.active.show();
44362             this.fireEvent("tabchange", this, this.active);
44363         }
44364         return tab;
44365     },
44366
44367     /**
44368      * Gets the active {@link Roo.TabPanelItem}.
44369      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
44370      */
44371     getActiveTab : function(){
44372         return this.active;
44373     },
44374
44375     /**
44376      * Updates the tab body element to fit the height of the container element
44377      * for overflow scrolling
44378      * @param {Number} targetHeight (optional) Override the starting height from the elements height
44379      */
44380     syncHeight : function(targetHeight){
44381         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44382         var bm = this.bodyEl.getMargins();
44383         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
44384         this.bodyEl.setHeight(newHeight);
44385         return newHeight;
44386     },
44387
44388     onResize : function(){
44389         if(this.monitorResize){
44390             this.autoSizeTabs();
44391         }
44392     },
44393
44394     /**
44395      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
44396      */
44397     beginUpdate : function(){
44398         this.updating = true;
44399     },
44400
44401     /**
44402      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
44403      */
44404     endUpdate : function(){
44405         this.updating = false;
44406         this.autoSizeTabs();
44407     },
44408
44409     /**
44410      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
44411      */
44412     autoSizeTabs : function()
44413     {
44414         var count = this.items.length;
44415         var vcount = count - this.hiddenCount;
44416         
44417         if (vcount < 2) {
44418             this.stripEl.hide();
44419         } else {
44420             this.stripEl.show();
44421         }
44422         
44423         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
44424             return;
44425         }
44426         
44427         
44428         var w = Math.max(this.el.getWidth() - this.cpad, 10);
44429         var availWidth = Math.floor(w / vcount);
44430         var b = this.stripBody;
44431         if(b.getWidth() > w){
44432             var tabs = this.items;
44433             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
44434             if(availWidth < this.minTabWidth){
44435                 /*if(!this.sleft){    // incomplete scrolling code
44436                     this.createScrollButtons();
44437                 }
44438                 this.showScroll();
44439                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
44440             }
44441         }else{
44442             if(this.currentTabWidth < this.preferredTabWidth){
44443                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
44444             }
44445         }
44446     },
44447
44448     /**
44449      * Returns the number of tabs in this TabPanel.
44450      * @return {Number}
44451      */
44452      getCount : function(){
44453          return this.items.length;
44454      },
44455
44456     /**
44457      * Resizes all the tabs to the passed width
44458      * @param {Number} The new width
44459      */
44460     setTabWidth : function(width){
44461         this.currentTabWidth = width;
44462         for(var i = 0, len = this.items.length; i < len; i++) {
44463                 if(!this.items[i].isHidden()) {
44464                 this.items[i].setWidth(width);
44465             }
44466         }
44467     },
44468
44469     /**
44470      * Destroys this TabPanel
44471      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
44472      */
44473     destroy : function(removeEl){
44474         Roo.EventManager.removeResizeListener(this.onResize, this);
44475         for(var i = 0, len = this.items.length; i < len; i++){
44476             this.items[i].purgeListeners();
44477         }
44478         if(removeEl === true){
44479             this.el.update("");
44480             this.el.remove();
44481         }
44482     },
44483     
44484     createStrip : function(container)
44485     {
44486         var strip = document.createElement("nav");
44487         strip.className = Roo.bootstrap.version == 4 ?
44488             "navbar-light bg-light" : 
44489             "navbar navbar-default"; //"x-tabs-wrap";
44490         container.appendChild(strip);
44491         return strip;
44492     },
44493     
44494     createStripList : function(strip)
44495     {
44496         // div wrapper for retard IE
44497         // returns the "tr" element.
44498         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
44499         //'<div class="x-tabs-strip-wrap">'+
44500           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
44501           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
44502         return strip.firstChild; //.firstChild.firstChild.firstChild;
44503     },
44504     createBody : function(container)
44505     {
44506         var body = document.createElement("div");
44507         Roo.id(body, "tab-body");
44508         //Roo.fly(body).addClass("x-tabs-body");
44509         Roo.fly(body).addClass("tab-content");
44510         container.appendChild(body);
44511         return body;
44512     },
44513     createItemBody :function(bodyEl, id){
44514         var body = Roo.getDom(id);
44515         if(!body){
44516             body = document.createElement("div");
44517             body.id = id;
44518         }
44519         //Roo.fly(body).addClass("x-tabs-item-body");
44520         Roo.fly(body).addClass("tab-pane");
44521          bodyEl.insertBefore(body, bodyEl.firstChild);
44522         return body;
44523     },
44524     /** @private */
44525     createStripElements :  function(stripEl, text, closable, tpl)
44526     {
44527         var td = document.createElement("li"); // was td..
44528         td.className = 'nav-item';
44529         
44530         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
44531         
44532         
44533         stripEl.appendChild(td);
44534         /*if(closable){
44535             td.className = "x-tabs-closable";
44536             if(!this.closeTpl){
44537                 this.closeTpl = new Roo.Template(
44538                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44539                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
44540                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
44541                 );
44542             }
44543             var el = this.closeTpl.overwrite(td, {"text": text});
44544             var close = el.getElementsByTagName("div")[0];
44545             var inner = el.getElementsByTagName("em")[0];
44546             return {"el": el, "close": close, "inner": inner};
44547         } else {
44548         */
44549         // not sure what this is..
44550 //            if(!this.tabTpl){
44551                 //this.tabTpl = new Roo.Template(
44552                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44553                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
44554                 //);
44555 //                this.tabTpl = new Roo.Template(
44556 //                   '<a href="#">' +
44557 //                   '<span unselectable="on"' +
44558 //                            (this.disableTooltips ? '' : ' title="{text}"') +
44559 //                            ' >{text}</span></a>'
44560 //                );
44561 //                
44562 //            }
44563
44564
44565             var template = tpl || this.tabTpl || false;
44566             
44567             if(!template){
44568                 template =  new Roo.Template(
44569                         Roo.bootstrap.version == 4 ? 
44570                             (
44571                                 '<a class="nav-link" href="#" unselectable="on"' +
44572                                      (this.disableTooltips ? '' : ' title="{text}"') +
44573                                      ' >{text}</a>'
44574                             ) : (
44575                                 '<a class="nav-link" href="#">' +
44576                                 '<span unselectable="on"' +
44577                                          (this.disableTooltips ? '' : ' title="{text}"') +
44578                                     ' >{text}</span></a>'
44579                             )
44580                 );
44581             }
44582             
44583             switch (typeof(template)) {
44584                 case 'object' :
44585                     break;
44586                 case 'string' :
44587                     template = new Roo.Template(template);
44588                     break;
44589                 default :
44590                     break;
44591             }
44592             
44593             var el = template.overwrite(td, {"text": text});
44594             
44595             var inner = el.getElementsByTagName("span")[0];
44596             
44597             return {"el": el, "inner": inner};
44598             
44599     }
44600         
44601     
44602 });
44603
44604 /**
44605  * @class Roo.TabPanelItem
44606  * @extends Roo.util.Observable
44607  * Represents an individual item (tab plus body) in a TabPanel.
44608  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
44609  * @param {String} id The id of this TabPanelItem
44610  * @param {String} text The text for the tab of this TabPanelItem
44611  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
44612  */
44613 Roo.bootstrap.panel.TabItem = function(config){
44614     /**
44615      * The {@link Roo.TabPanel} this TabPanelItem belongs to
44616      * @type Roo.TabPanel
44617      */
44618     this.tabPanel = config.panel;
44619     /**
44620      * The id for this TabPanelItem
44621      * @type String
44622      */
44623     this.id = config.id;
44624     /** @private */
44625     this.disabled = false;
44626     /** @private */
44627     this.text = config.text;
44628     /** @private */
44629     this.loaded = false;
44630     this.closable = config.closable;
44631
44632     /**
44633      * The body element for this TabPanelItem.
44634      * @type Roo.Element
44635      */
44636     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
44637     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
44638     this.bodyEl.setStyle("display", "block");
44639     this.bodyEl.setStyle("zoom", "1");
44640     //this.hideAction();
44641
44642     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
44643     /** @private */
44644     this.el = Roo.get(els.el);
44645     this.inner = Roo.get(els.inner, true);
44646      this.textEl = Roo.bootstrap.version == 4 ?
44647         this.el : Roo.get(this.el.dom.firstChild, true);
44648
44649     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
44650     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
44651
44652     
44653 //    this.el.on("mousedown", this.onTabMouseDown, this);
44654     this.el.on("click", this.onTabClick, this);
44655     /** @private */
44656     if(config.closable){
44657         var c = Roo.get(els.close, true);
44658         c.dom.title = this.closeText;
44659         c.addClassOnOver("close-over");
44660         c.on("click", this.closeClick, this);
44661      }
44662
44663     this.addEvents({
44664          /**
44665          * @event activate
44666          * Fires when this tab becomes the active tab.
44667          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44668          * @param {Roo.TabPanelItem} this
44669          */
44670         "activate": true,
44671         /**
44672          * @event beforeclose
44673          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
44674          * @param {Roo.TabPanelItem} this
44675          * @param {Object} e Set cancel to true on this object to cancel the close.
44676          */
44677         "beforeclose": true,
44678         /**
44679          * @event close
44680          * Fires when this tab is closed.
44681          * @param {Roo.TabPanelItem} this
44682          */
44683          "close": true,
44684         /**
44685          * @event deactivate
44686          * Fires when this tab is no longer the active tab.
44687          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44688          * @param {Roo.TabPanelItem} this
44689          */
44690          "deactivate" : true
44691     });
44692     this.hidden = false;
44693
44694     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
44695 };
44696
44697 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
44698            {
44699     purgeListeners : function(){
44700        Roo.util.Observable.prototype.purgeListeners.call(this);
44701        this.el.removeAllListeners();
44702     },
44703     /**
44704      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
44705      */
44706     show : function(){
44707         this.status_node.addClass("active");
44708         this.showAction();
44709         if(Roo.isOpera){
44710             this.tabPanel.stripWrap.repaint();
44711         }
44712         this.fireEvent("activate", this.tabPanel, this);
44713     },
44714
44715     /**
44716      * Returns true if this tab is the active tab.
44717      * @return {Boolean}
44718      */
44719     isActive : function(){
44720         return this.tabPanel.getActiveTab() == this;
44721     },
44722
44723     /**
44724      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
44725      */
44726     hide : function(){
44727         this.status_node.removeClass("active");
44728         this.hideAction();
44729         this.fireEvent("deactivate", this.tabPanel, this);
44730     },
44731
44732     hideAction : function(){
44733         this.bodyEl.hide();
44734         this.bodyEl.setStyle("position", "absolute");
44735         this.bodyEl.setLeft("-20000px");
44736         this.bodyEl.setTop("-20000px");
44737     },
44738
44739     showAction : function(){
44740         this.bodyEl.setStyle("position", "relative");
44741         this.bodyEl.setTop("");
44742         this.bodyEl.setLeft("");
44743         this.bodyEl.show();
44744     },
44745
44746     /**
44747      * Set the tooltip for the tab.
44748      * @param {String} tooltip The tab's tooltip
44749      */
44750     setTooltip : function(text){
44751         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
44752             this.textEl.dom.qtip = text;
44753             this.textEl.dom.removeAttribute('title');
44754         }else{
44755             this.textEl.dom.title = text;
44756         }
44757     },
44758
44759     onTabClick : function(e){
44760         e.preventDefault();
44761         this.tabPanel.activate(this.id);
44762     },
44763
44764     onTabMouseDown : function(e){
44765         e.preventDefault();
44766         this.tabPanel.activate(this.id);
44767     },
44768 /*
44769     getWidth : function(){
44770         return this.inner.getWidth();
44771     },
44772
44773     setWidth : function(width){
44774         var iwidth = width - this.linode.getPadding("lr");
44775         this.inner.setWidth(iwidth);
44776         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
44777         this.linode.setWidth(width);
44778     },
44779 */
44780     /**
44781      * Show or hide the tab
44782      * @param {Boolean} hidden True to hide or false to show.
44783      */
44784     setHidden : function(hidden){
44785         this.hidden = hidden;
44786         this.linode.setStyle("display", hidden ? "none" : "");
44787     },
44788
44789     /**
44790      * Returns true if this tab is "hidden"
44791      * @return {Boolean}
44792      */
44793     isHidden : function(){
44794         return this.hidden;
44795     },
44796
44797     /**
44798      * Returns the text for this tab
44799      * @return {String}
44800      */
44801     getText : function(){
44802         return this.text;
44803     },
44804     /*
44805     autoSize : function(){
44806         //this.el.beginMeasure();
44807         this.textEl.setWidth(1);
44808         /*
44809          *  #2804 [new] Tabs in Roojs
44810          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
44811          */
44812         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
44813         //this.el.endMeasure();
44814     //},
44815
44816     /**
44817      * Sets the text for the tab (Note: this also sets the tooltip text)
44818      * @param {String} text The tab's text and tooltip
44819      */
44820     setText : function(text){
44821         this.text = text;
44822         this.textEl.update(text);
44823         this.setTooltip(text);
44824         //if(!this.tabPanel.resizeTabs){
44825         //    this.autoSize();
44826         //}
44827     },
44828     /**
44829      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
44830      */
44831     activate : function(){
44832         this.tabPanel.activate(this.id);
44833     },
44834
44835     /**
44836      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
44837      */
44838     disable : function(){
44839         if(this.tabPanel.active != this){
44840             this.disabled = true;
44841             this.status_node.addClass("disabled");
44842         }
44843     },
44844
44845     /**
44846      * Enables this TabPanelItem if it was previously disabled.
44847      */
44848     enable : function(){
44849         this.disabled = false;
44850         this.status_node.removeClass("disabled");
44851     },
44852
44853     /**
44854      * Sets the content for this TabPanelItem.
44855      * @param {String} content The content
44856      * @param {Boolean} loadScripts true to look for and load scripts
44857      */
44858     setContent : function(content, loadScripts){
44859         this.bodyEl.update(content, loadScripts);
44860     },
44861
44862     /**
44863      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
44864      * @return {Roo.UpdateManager} The UpdateManager
44865      */
44866     getUpdateManager : function(){
44867         return this.bodyEl.getUpdateManager();
44868     },
44869
44870     /**
44871      * Set a URL to be used to load the content for this TabPanelItem.
44872      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
44873      * @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)
44874      * @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)
44875      * @return {Roo.UpdateManager} The UpdateManager
44876      */
44877     setUrl : function(url, params, loadOnce){
44878         if(this.refreshDelegate){
44879             this.un('activate', this.refreshDelegate);
44880         }
44881         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44882         this.on("activate", this.refreshDelegate);
44883         return this.bodyEl.getUpdateManager();
44884     },
44885
44886     /** @private */
44887     _handleRefresh : function(url, params, loadOnce){
44888         if(!loadOnce || !this.loaded){
44889             var updater = this.bodyEl.getUpdateManager();
44890             updater.update(url, params, this._setLoaded.createDelegate(this));
44891         }
44892     },
44893
44894     /**
44895      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
44896      *   Will fail silently if the setUrl method has not been called.
44897      *   This does not activate the panel, just updates its content.
44898      */
44899     refresh : function(){
44900         if(this.refreshDelegate){
44901            this.loaded = false;
44902            this.refreshDelegate();
44903         }
44904     },
44905
44906     /** @private */
44907     _setLoaded : function(){
44908         this.loaded = true;
44909     },
44910
44911     /** @private */
44912     closeClick : function(e){
44913         var o = {};
44914         e.stopEvent();
44915         this.fireEvent("beforeclose", this, o);
44916         if(o.cancel !== true){
44917             this.tabPanel.removeTab(this.id);
44918         }
44919     },
44920     /**
44921      * The text displayed in the tooltip for the close icon.
44922      * @type String
44923      */
44924     closeText : "Close this tab"
44925 });
44926 /**
44927 *    This script refer to:
44928 *    Title: International Telephone Input
44929 *    Author: Jack O'Connor
44930 *    Code version:  v12.1.12
44931 *    Availability: https://github.com/jackocnr/intl-tel-input.git
44932 **/
44933
44934 Roo.bootstrap.form.PhoneInputData = function() {
44935     var d = [
44936       [
44937         "Afghanistan (‫افغانستان‬‎)",
44938         "af",
44939         "93"
44940       ],
44941       [
44942         "Albania (Shqipëri)",
44943         "al",
44944         "355"
44945       ],
44946       [
44947         "Algeria (‫الجزائر‬‎)",
44948         "dz",
44949         "213"
44950       ],
44951       [
44952         "American Samoa",
44953         "as",
44954         "1684"
44955       ],
44956       [
44957         "Andorra",
44958         "ad",
44959         "376"
44960       ],
44961       [
44962         "Angola",
44963         "ao",
44964         "244"
44965       ],
44966       [
44967         "Anguilla",
44968         "ai",
44969         "1264"
44970       ],
44971       [
44972         "Antigua and Barbuda",
44973         "ag",
44974         "1268"
44975       ],
44976       [
44977         "Argentina",
44978         "ar",
44979         "54"
44980       ],
44981       [
44982         "Armenia (Հայաստան)",
44983         "am",
44984         "374"
44985       ],
44986       [
44987         "Aruba",
44988         "aw",
44989         "297"
44990       ],
44991       [
44992         "Australia",
44993         "au",
44994         "61",
44995         0
44996       ],
44997       [
44998         "Austria (Österreich)",
44999         "at",
45000         "43"
45001       ],
45002       [
45003         "Azerbaijan (Azərbaycan)",
45004         "az",
45005         "994"
45006       ],
45007       [
45008         "Bahamas",
45009         "bs",
45010         "1242"
45011       ],
45012       [
45013         "Bahrain (‫البحرين‬‎)",
45014         "bh",
45015         "973"
45016       ],
45017       [
45018         "Bangladesh (বাংলাদেশ)",
45019         "bd",
45020         "880"
45021       ],
45022       [
45023         "Barbados",
45024         "bb",
45025         "1246"
45026       ],
45027       [
45028         "Belarus (Беларусь)",
45029         "by",
45030         "375"
45031       ],
45032       [
45033         "Belgium (België)",
45034         "be",
45035         "32"
45036       ],
45037       [
45038         "Belize",
45039         "bz",
45040         "501"
45041       ],
45042       [
45043         "Benin (Bénin)",
45044         "bj",
45045         "229"
45046       ],
45047       [
45048         "Bermuda",
45049         "bm",
45050         "1441"
45051       ],
45052       [
45053         "Bhutan (འབྲུག)",
45054         "bt",
45055         "975"
45056       ],
45057       [
45058         "Bolivia",
45059         "bo",
45060         "591"
45061       ],
45062       [
45063         "Bosnia and Herzegovina (Босна и Херцеговина)",
45064         "ba",
45065         "387"
45066       ],
45067       [
45068         "Botswana",
45069         "bw",
45070         "267"
45071       ],
45072       [
45073         "Brazil (Brasil)",
45074         "br",
45075         "55"
45076       ],
45077       [
45078         "British Indian Ocean Territory",
45079         "io",
45080         "246"
45081       ],
45082       [
45083         "British Virgin Islands",
45084         "vg",
45085         "1284"
45086       ],
45087       [
45088         "Brunei",
45089         "bn",
45090         "673"
45091       ],
45092       [
45093         "Bulgaria (България)",
45094         "bg",
45095         "359"
45096       ],
45097       [
45098         "Burkina Faso",
45099         "bf",
45100         "226"
45101       ],
45102       [
45103         "Burundi (Uburundi)",
45104         "bi",
45105         "257"
45106       ],
45107       [
45108         "Cambodia (កម្ពុជា)",
45109         "kh",
45110         "855"
45111       ],
45112       [
45113         "Cameroon (Cameroun)",
45114         "cm",
45115         "237"
45116       ],
45117       [
45118         "Canada",
45119         "ca",
45120         "1",
45121         1,
45122         ["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"]
45123       ],
45124       [
45125         "Cape Verde (Kabu Verdi)",
45126         "cv",
45127         "238"
45128       ],
45129       [
45130         "Caribbean Netherlands",
45131         "bq",
45132         "599",
45133         1
45134       ],
45135       [
45136         "Cayman Islands",
45137         "ky",
45138         "1345"
45139       ],
45140       [
45141         "Central African Republic (République centrafricaine)",
45142         "cf",
45143         "236"
45144       ],
45145       [
45146         "Chad (Tchad)",
45147         "td",
45148         "235"
45149       ],
45150       [
45151         "Chile",
45152         "cl",
45153         "56"
45154       ],
45155       [
45156         "China (中国)",
45157         "cn",
45158         "86"
45159       ],
45160       [
45161         "Christmas Island",
45162         "cx",
45163         "61",
45164         2
45165       ],
45166       [
45167         "Cocos (Keeling) Islands",
45168         "cc",
45169         "61",
45170         1
45171       ],
45172       [
45173         "Colombia",
45174         "co",
45175         "57"
45176       ],
45177       [
45178         "Comoros (‫جزر القمر‬‎)",
45179         "km",
45180         "269"
45181       ],
45182       [
45183         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
45184         "cd",
45185         "243"
45186       ],
45187       [
45188         "Congo (Republic) (Congo-Brazzaville)",
45189         "cg",
45190         "242"
45191       ],
45192       [
45193         "Cook Islands",
45194         "ck",
45195         "682"
45196       ],
45197       [
45198         "Costa Rica",
45199         "cr",
45200         "506"
45201       ],
45202       [
45203         "Côte d’Ivoire",
45204         "ci",
45205         "225"
45206       ],
45207       [
45208         "Croatia (Hrvatska)",
45209         "hr",
45210         "385"
45211       ],
45212       [
45213         "Cuba",
45214         "cu",
45215         "53"
45216       ],
45217       [
45218         "Curaçao",
45219         "cw",
45220         "599",
45221         0
45222       ],
45223       [
45224         "Cyprus (Κύπρος)",
45225         "cy",
45226         "357"
45227       ],
45228       [
45229         "Czech Republic (Česká republika)",
45230         "cz",
45231         "420"
45232       ],
45233       [
45234         "Denmark (Danmark)",
45235         "dk",
45236         "45"
45237       ],
45238       [
45239         "Djibouti",
45240         "dj",
45241         "253"
45242       ],
45243       [
45244         "Dominica",
45245         "dm",
45246         "1767"
45247       ],
45248       [
45249         "Dominican Republic (República Dominicana)",
45250         "do",
45251         "1",
45252         2,
45253         ["809", "829", "849"]
45254       ],
45255       [
45256         "Ecuador",
45257         "ec",
45258         "593"
45259       ],
45260       [
45261         "Egypt (‫مصر‬‎)",
45262         "eg",
45263         "20"
45264       ],
45265       [
45266         "El Salvador",
45267         "sv",
45268         "503"
45269       ],
45270       [
45271         "Equatorial Guinea (Guinea Ecuatorial)",
45272         "gq",
45273         "240"
45274       ],
45275       [
45276         "Eritrea",
45277         "er",
45278         "291"
45279       ],
45280       [
45281         "Estonia (Eesti)",
45282         "ee",
45283         "372"
45284       ],
45285       [
45286         "Ethiopia",
45287         "et",
45288         "251"
45289       ],
45290       [
45291         "Falkland Islands (Islas Malvinas)",
45292         "fk",
45293         "500"
45294       ],
45295       [
45296         "Faroe Islands (Føroyar)",
45297         "fo",
45298         "298"
45299       ],
45300       [
45301         "Fiji",
45302         "fj",
45303         "679"
45304       ],
45305       [
45306         "Finland (Suomi)",
45307         "fi",
45308         "358",
45309         0
45310       ],
45311       [
45312         "France",
45313         "fr",
45314         "33"
45315       ],
45316       [
45317         "French Guiana (Guyane française)",
45318         "gf",
45319         "594"
45320       ],
45321       [
45322         "French Polynesia (Polynésie française)",
45323         "pf",
45324         "689"
45325       ],
45326       [
45327         "Gabon",
45328         "ga",
45329         "241"
45330       ],
45331       [
45332         "Gambia",
45333         "gm",
45334         "220"
45335       ],
45336       [
45337         "Georgia (საქართველო)",
45338         "ge",
45339         "995"
45340       ],
45341       [
45342         "Germany (Deutschland)",
45343         "de",
45344         "49"
45345       ],
45346       [
45347         "Ghana (Gaana)",
45348         "gh",
45349         "233"
45350       ],
45351       [
45352         "Gibraltar",
45353         "gi",
45354         "350"
45355       ],
45356       [
45357         "Greece (Ελλάδα)",
45358         "gr",
45359         "30"
45360       ],
45361       [
45362         "Greenland (Kalaallit Nunaat)",
45363         "gl",
45364         "299"
45365       ],
45366       [
45367         "Grenada",
45368         "gd",
45369         "1473"
45370       ],
45371       [
45372         "Guadeloupe",
45373         "gp",
45374         "590",
45375         0
45376       ],
45377       [
45378         "Guam",
45379         "gu",
45380         "1671"
45381       ],
45382       [
45383         "Guatemala",
45384         "gt",
45385         "502"
45386       ],
45387       [
45388         "Guernsey",
45389         "gg",
45390         "44",
45391         1
45392       ],
45393       [
45394         "Guinea (Guinée)",
45395         "gn",
45396         "224"
45397       ],
45398       [
45399         "Guinea-Bissau (Guiné Bissau)",
45400         "gw",
45401         "245"
45402       ],
45403       [
45404         "Guyana",
45405         "gy",
45406         "592"
45407       ],
45408       [
45409         "Haiti",
45410         "ht",
45411         "509"
45412       ],
45413       [
45414         "Honduras",
45415         "hn",
45416         "504"
45417       ],
45418       [
45419         "Hong Kong (香港)",
45420         "hk",
45421         "852"
45422       ],
45423       [
45424         "Hungary (Magyarország)",
45425         "hu",
45426         "36"
45427       ],
45428       [
45429         "Iceland (Ísland)",
45430         "is",
45431         "354"
45432       ],
45433       [
45434         "India (भारत)",
45435         "in",
45436         "91"
45437       ],
45438       [
45439         "Indonesia",
45440         "id",
45441         "62"
45442       ],
45443       [
45444         "Iran (‫ایران‬‎)",
45445         "ir",
45446         "98"
45447       ],
45448       [
45449         "Iraq (‫العراق‬‎)",
45450         "iq",
45451         "964"
45452       ],
45453       [
45454         "Ireland",
45455         "ie",
45456         "353"
45457       ],
45458       [
45459         "Isle of Man",
45460         "im",
45461         "44",
45462         2
45463       ],
45464       [
45465         "Israel (‫ישראל‬‎)",
45466         "il",
45467         "972"
45468       ],
45469       [
45470         "Italy (Italia)",
45471         "it",
45472         "39",
45473         0
45474       ],
45475       [
45476         "Jamaica",
45477         "jm",
45478         "1876"
45479       ],
45480       [
45481         "Japan (日本)",
45482         "jp",
45483         "81"
45484       ],
45485       [
45486         "Jersey",
45487         "je",
45488         "44",
45489         3
45490       ],
45491       [
45492         "Jordan (‫الأردن‬‎)",
45493         "jo",
45494         "962"
45495       ],
45496       [
45497         "Kazakhstan (Казахстан)",
45498         "kz",
45499         "7",
45500         1
45501       ],
45502       [
45503         "Kenya",
45504         "ke",
45505         "254"
45506       ],
45507       [
45508         "Kiribati",
45509         "ki",
45510         "686"
45511       ],
45512       [
45513         "Kosovo",
45514         "xk",
45515         "383"
45516       ],
45517       [
45518         "Kuwait (‫الكويت‬‎)",
45519         "kw",
45520         "965"
45521       ],
45522       [
45523         "Kyrgyzstan (Кыргызстан)",
45524         "kg",
45525         "996"
45526       ],
45527       [
45528         "Laos (ລາວ)",
45529         "la",
45530         "856"
45531       ],
45532       [
45533         "Latvia (Latvija)",
45534         "lv",
45535         "371"
45536       ],
45537       [
45538         "Lebanon (‫لبنان‬‎)",
45539         "lb",
45540         "961"
45541       ],
45542       [
45543         "Lesotho",
45544         "ls",
45545         "266"
45546       ],
45547       [
45548         "Liberia",
45549         "lr",
45550         "231"
45551       ],
45552       [
45553         "Libya (‫ليبيا‬‎)",
45554         "ly",
45555         "218"
45556       ],
45557       [
45558         "Liechtenstein",
45559         "li",
45560         "423"
45561       ],
45562       [
45563         "Lithuania (Lietuva)",
45564         "lt",
45565         "370"
45566       ],
45567       [
45568         "Luxembourg",
45569         "lu",
45570         "352"
45571       ],
45572       [
45573         "Macau (澳門)",
45574         "mo",
45575         "853"
45576       ],
45577       [
45578         "Macedonia (FYROM) (Македонија)",
45579         "mk",
45580         "389"
45581       ],
45582       [
45583         "Madagascar (Madagasikara)",
45584         "mg",
45585         "261"
45586       ],
45587       [
45588         "Malawi",
45589         "mw",
45590         "265"
45591       ],
45592       [
45593         "Malaysia",
45594         "my",
45595         "60"
45596       ],
45597       [
45598         "Maldives",
45599         "mv",
45600         "960"
45601       ],
45602       [
45603         "Mali",
45604         "ml",
45605         "223"
45606       ],
45607       [
45608         "Malta",
45609         "mt",
45610         "356"
45611       ],
45612       [
45613         "Marshall Islands",
45614         "mh",
45615         "692"
45616       ],
45617       [
45618         "Martinique",
45619         "mq",
45620         "596"
45621       ],
45622       [
45623         "Mauritania (‫موريتانيا‬‎)",
45624         "mr",
45625         "222"
45626       ],
45627       [
45628         "Mauritius (Moris)",
45629         "mu",
45630         "230"
45631       ],
45632       [
45633         "Mayotte",
45634         "yt",
45635         "262",
45636         1
45637       ],
45638       [
45639         "Mexico (México)",
45640         "mx",
45641         "52"
45642       ],
45643       [
45644         "Micronesia",
45645         "fm",
45646         "691"
45647       ],
45648       [
45649         "Moldova (Republica Moldova)",
45650         "md",
45651         "373"
45652       ],
45653       [
45654         "Monaco",
45655         "mc",
45656         "377"
45657       ],
45658       [
45659         "Mongolia (Монгол)",
45660         "mn",
45661         "976"
45662       ],
45663       [
45664         "Montenegro (Crna Gora)",
45665         "me",
45666         "382"
45667       ],
45668       [
45669         "Montserrat",
45670         "ms",
45671         "1664"
45672       ],
45673       [
45674         "Morocco (‫المغرب‬‎)",
45675         "ma",
45676         "212",
45677         0
45678       ],
45679       [
45680         "Mozambique (Moçambique)",
45681         "mz",
45682         "258"
45683       ],
45684       [
45685         "Myanmar (Burma) (မြန်မာ)",
45686         "mm",
45687         "95"
45688       ],
45689       [
45690         "Namibia (Namibië)",
45691         "na",
45692         "264"
45693       ],
45694       [
45695         "Nauru",
45696         "nr",
45697         "674"
45698       ],
45699       [
45700         "Nepal (नेपाल)",
45701         "np",
45702         "977"
45703       ],
45704       [
45705         "Netherlands (Nederland)",
45706         "nl",
45707         "31"
45708       ],
45709       [
45710         "New Caledonia (Nouvelle-Calédonie)",
45711         "nc",
45712         "687"
45713       ],
45714       [
45715         "New Zealand",
45716         "nz",
45717         "64"
45718       ],
45719       [
45720         "Nicaragua",
45721         "ni",
45722         "505"
45723       ],
45724       [
45725         "Niger (Nijar)",
45726         "ne",
45727         "227"
45728       ],
45729       [
45730         "Nigeria",
45731         "ng",
45732         "234"
45733       ],
45734       [
45735         "Niue",
45736         "nu",
45737         "683"
45738       ],
45739       [
45740         "Norfolk Island",
45741         "nf",
45742         "672"
45743       ],
45744       [
45745         "North Korea (조선 민주주의 인민 공화국)",
45746         "kp",
45747         "850"
45748       ],
45749       [
45750         "Northern Mariana Islands",
45751         "mp",
45752         "1670"
45753       ],
45754       [
45755         "Norway (Norge)",
45756         "no",
45757         "47",
45758         0
45759       ],
45760       [
45761         "Oman (‫عُمان‬‎)",
45762         "om",
45763         "968"
45764       ],
45765       [
45766         "Pakistan (‫پاکستان‬‎)",
45767         "pk",
45768         "92"
45769       ],
45770       [
45771         "Palau",
45772         "pw",
45773         "680"
45774       ],
45775       [
45776         "Palestine (‫فلسطين‬‎)",
45777         "ps",
45778         "970"
45779       ],
45780       [
45781         "Panama (Panamá)",
45782         "pa",
45783         "507"
45784       ],
45785       [
45786         "Papua New Guinea",
45787         "pg",
45788         "675"
45789       ],
45790       [
45791         "Paraguay",
45792         "py",
45793         "595"
45794       ],
45795       [
45796         "Peru (Perú)",
45797         "pe",
45798         "51"
45799       ],
45800       [
45801         "Philippines",
45802         "ph",
45803         "63"
45804       ],
45805       [
45806         "Poland (Polska)",
45807         "pl",
45808         "48"
45809       ],
45810       [
45811         "Portugal",
45812         "pt",
45813         "351"
45814       ],
45815       [
45816         "Puerto Rico",
45817         "pr",
45818         "1",
45819         3,
45820         ["787", "939"]
45821       ],
45822       [
45823         "Qatar (‫قطر‬‎)",
45824         "qa",
45825         "974"
45826       ],
45827       [
45828         "Réunion (La Réunion)",
45829         "re",
45830         "262",
45831         0
45832       ],
45833       [
45834         "Romania (România)",
45835         "ro",
45836         "40"
45837       ],
45838       [
45839         "Russia (Россия)",
45840         "ru",
45841         "7",
45842         0
45843       ],
45844       [
45845         "Rwanda",
45846         "rw",
45847         "250"
45848       ],
45849       [
45850         "Saint Barthélemy",
45851         "bl",
45852         "590",
45853         1
45854       ],
45855       [
45856         "Saint Helena",
45857         "sh",
45858         "290"
45859       ],
45860       [
45861         "Saint Kitts and Nevis",
45862         "kn",
45863         "1869"
45864       ],
45865       [
45866         "Saint Lucia",
45867         "lc",
45868         "1758"
45869       ],
45870       [
45871         "Saint Martin (Saint-Martin (partie française))",
45872         "mf",
45873         "590",
45874         2
45875       ],
45876       [
45877         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
45878         "pm",
45879         "508"
45880       ],
45881       [
45882         "Saint Vincent and the Grenadines",
45883         "vc",
45884         "1784"
45885       ],
45886       [
45887         "Samoa",
45888         "ws",
45889         "685"
45890       ],
45891       [
45892         "San Marino",
45893         "sm",
45894         "378"
45895       ],
45896       [
45897         "São Tomé and Príncipe (São Tomé e Príncipe)",
45898         "st",
45899         "239"
45900       ],
45901       [
45902         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
45903         "sa",
45904         "966"
45905       ],
45906       [
45907         "Senegal (Sénégal)",
45908         "sn",
45909         "221"
45910       ],
45911       [
45912         "Serbia (Србија)",
45913         "rs",
45914         "381"
45915       ],
45916       [
45917         "Seychelles",
45918         "sc",
45919         "248"
45920       ],
45921       [
45922         "Sierra Leone",
45923         "sl",
45924         "232"
45925       ],
45926       [
45927         "Singapore",
45928         "sg",
45929         "65"
45930       ],
45931       [
45932         "Sint Maarten",
45933         "sx",
45934         "1721"
45935       ],
45936       [
45937         "Slovakia (Slovensko)",
45938         "sk",
45939         "421"
45940       ],
45941       [
45942         "Slovenia (Slovenija)",
45943         "si",
45944         "386"
45945       ],
45946       [
45947         "Solomon Islands",
45948         "sb",
45949         "677"
45950       ],
45951       [
45952         "Somalia (Soomaaliya)",
45953         "so",
45954         "252"
45955       ],
45956       [
45957         "South Africa",
45958         "za",
45959         "27"
45960       ],
45961       [
45962         "South Korea (대한민국)",
45963         "kr",
45964         "82"
45965       ],
45966       [
45967         "South Sudan (‫جنوب السودان‬‎)",
45968         "ss",
45969         "211"
45970       ],
45971       [
45972         "Spain (España)",
45973         "es",
45974         "34"
45975       ],
45976       [
45977         "Sri Lanka (ශ්‍රී ලංකාව)",
45978         "lk",
45979         "94"
45980       ],
45981       [
45982         "Sudan (‫السودان‬‎)",
45983         "sd",
45984         "249"
45985       ],
45986       [
45987         "Suriname",
45988         "sr",
45989         "597"
45990       ],
45991       [
45992         "Svalbard and Jan Mayen",
45993         "sj",
45994         "47",
45995         1
45996       ],
45997       [
45998         "Swaziland",
45999         "sz",
46000         "268"
46001       ],
46002       [
46003         "Sweden (Sverige)",
46004         "se",
46005         "46"
46006       ],
46007       [
46008         "Switzerland (Schweiz)",
46009         "ch",
46010         "41"
46011       ],
46012       [
46013         "Syria (‫سوريا‬‎)",
46014         "sy",
46015         "963"
46016       ],
46017       [
46018         "Taiwan (台灣)",
46019         "tw",
46020         "886"
46021       ],
46022       [
46023         "Tajikistan",
46024         "tj",
46025         "992"
46026       ],
46027       [
46028         "Tanzania",
46029         "tz",
46030         "255"
46031       ],
46032       [
46033         "Thailand (ไทย)",
46034         "th",
46035         "66"
46036       ],
46037       [
46038         "Timor-Leste",
46039         "tl",
46040         "670"
46041       ],
46042       [
46043         "Togo",
46044         "tg",
46045         "228"
46046       ],
46047       [
46048         "Tokelau",
46049         "tk",
46050         "690"
46051       ],
46052       [
46053         "Tonga",
46054         "to",
46055         "676"
46056       ],
46057       [
46058         "Trinidad and Tobago",
46059         "tt",
46060         "1868"
46061       ],
46062       [
46063         "Tunisia (‫تونس‬‎)",
46064         "tn",
46065         "216"
46066       ],
46067       [
46068         "Turkey (Türkiye)",
46069         "tr",
46070         "90"
46071       ],
46072       [
46073         "Turkmenistan",
46074         "tm",
46075         "993"
46076       ],
46077       [
46078         "Turks and Caicos Islands",
46079         "tc",
46080         "1649"
46081       ],
46082       [
46083         "Tuvalu",
46084         "tv",
46085         "688"
46086       ],
46087       [
46088         "U.S. Virgin Islands",
46089         "vi",
46090         "1340"
46091       ],
46092       [
46093         "Uganda",
46094         "ug",
46095         "256"
46096       ],
46097       [
46098         "Ukraine (Україна)",
46099         "ua",
46100         "380"
46101       ],
46102       [
46103         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
46104         "ae",
46105         "971"
46106       ],
46107       [
46108         "United Kingdom",
46109         "gb",
46110         "44",
46111         0
46112       ],
46113       [
46114         "United States",
46115         "us",
46116         "1",
46117         0
46118       ],
46119       [
46120         "Uruguay",
46121         "uy",
46122         "598"
46123       ],
46124       [
46125         "Uzbekistan (Oʻzbekiston)",
46126         "uz",
46127         "998"
46128       ],
46129       [
46130         "Vanuatu",
46131         "vu",
46132         "678"
46133       ],
46134       [
46135         "Vatican City (Città del Vaticano)",
46136         "va",
46137         "39",
46138         1
46139       ],
46140       [
46141         "Venezuela",
46142         "ve",
46143         "58"
46144       ],
46145       [
46146         "Vietnam (Việt Nam)",
46147         "vn",
46148         "84"
46149       ],
46150       [
46151         "Wallis and Futuna (Wallis-et-Futuna)",
46152         "wf",
46153         "681"
46154       ],
46155       [
46156         "Western Sahara (‫الصحراء الغربية‬‎)",
46157         "eh",
46158         "212",
46159         1
46160       ],
46161       [
46162         "Yemen (‫اليمن‬‎)",
46163         "ye",
46164         "967"
46165       ],
46166       [
46167         "Zambia",
46168         "zm",
46169         "260"
46170       ],
46171       [
46172         "Zimbabwe",
46173         "zw",
46174         "263"
46175       ],
46176       [
46177         "Åland Islands",
46178         "ax",
46179         "358",
46180         1
46181       ]
46182   ];
46183   
46184   return d;
46185 }/**
46186 *    This script refer to:
46187 *    Title: International Telephone Input
46188 *    Author: Jack O'Connor
46189 *    Code version:  v12.1.12
46190 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46191 **/
46192
46193 /**
46194  * @class Roo.bootstrap.form.PhoneInput
46195  * @extends Roo.bootstrap.form.TriggerField
46196  * An input with International dial-code selection
46197  
46198  * @cfg {String} defaultDialCode default '+852'
46199  * @cfg {Array} preferedCountries default []
46200   
46201  * @constructor
46202  * Create a new PhoneInput.
46203  * @param {Object} config Configuration options
46204  */
46205
46206 Roo.bootstrap.form.PhoneInput = function(config) {
46207     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
46208 };
46209
46210 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
46211         /**
46212         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
46213         */
46214         listWidth: undefined,
46215         
46216         selectedClass: 'active',
46217         
46218         invalidClass : "has-warning",
46219         
46220         validClass: 'has-success',
46221         
46222         allowed: '0123456789',
46223         
46224         max_length: 15,
46225         
46226         /**
46227          * @cfg {String} defaultDialCode The default dial code when initializing the input
46228          */
46229         defaultDialCode: '+852',
46230         
46231         /**
46232          * @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
46233          */
46234         preferedCountries: false,
46235         
46236         getAutoCreate : function()
46237         {
46238             var data = Roo.bootstrap.form.PhoneInputData();
46239             var align = this.labelAlign || this.parentLabelAlign();
46240             var id = Roo.id();
46241             
46242             this.allCountries = [];
46243             this.dialCodeMapping = [];
46244             
46245             for (var i = 0; i < data.length; i++) {
46246               var c = data[i];
46247               this.allCountries[i] = {
46248                 name: c[0],
46249                 iso2: c[1],
46250                 dialCode: c[2],
46251                 priority: c[3] || 0,
46252                 areaCodes: c[4] || null
46253               };
46254               this.dialCodeMapping[c[2]] = {
46255                   name: c[0],
46256                   iso2: c[1],
46257                   priority: c[3] || 0,
46258                   areaCodes: c[4] || null
46259               };
46260             }
46261             
46262             var cfg = {
46263                 cls: 'form-group',
46264                 cn: []
46265             };
46266             
46267             var input =  {
46268                 tag: 'input',
46269                 id : id,
46270                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
46271                 maxlength: this.max_length,
46272                 cls : 'form-control tel-input',
46273                 autocomplete: 'new-password'
46274             };
46275             
46276             var hiddenInput = {
46277                 tag: 'input',
46278                 type: 'hidden',
46279                 cls: 'hidden-tel-input'
46280             };
46281             
46282             if (this.name) {
46283                 hiddenInput.name = this.name;
46284             }
46285             
46286             if (this.disabled) {
46287                 input.disabled = true;
46288             }
46289             
46290             var flag_container = {
46291                 tag: 'div',
46292                 cls: 'flag-box',
46293                 cn: [
46294                     {
46295                         tag: 'div',
46296                         cls: 'flag'
46297                     },
46298                     {
46299                         tag: 'div',
46300                         cls: 'caret'
46301                     }
46302                 ]
46303             };
46304             
46305             var box = {
46306                 tag: 'div',
46307                 cls: this.hasFeedback ? 'has-feedback' : '',
46308                 cn: [
46309                     hiddenInput,
46310                     input,
46311                     {
46312                         tag: 'input',
46313                         cls: 'dial-code-holder',
46314                         disabled: true
46315                     }
46316                 ]
46317             };
46318             
46319             var container = {
46320                 cls: 'roo-select2-container input-group',
46321                 cn: [
46322                     flag_container,
46323                     box
46324                 ]
46325             };
46326             
46327             if (this.fieldLabel.length) {
46328                 var indicator = {
46329                     tag: 'i',
46330                     tooltip: 'This field is required'
46331                 };
46332                 
46333                 var label = {
46334                     tag: 'label',
46335                     'for':  id,
46336                     cls: 'control-label',
46337                     cn: []
46338                 };
46339                 
46340                 var label_text = {
46341                     tag: 'span',
46342                     html: this.fieldLabel
46343                 };
46344                 
46345                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46346                 label.cn = [
46347                     indicator,
46348                     label_text
46349                 ];
46350                 
46351                 if(this.indicatorpos == 'right') {
46352                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46353                     label.cn = [
46354                         label_text,
46355                         indicator
46356                     ];
46357                 }
46358                 
46359                 if(align == 'left') {
46360                     container = {
46361                         tag: 'div',
46362                         cn: [
46363                             container
46364                         ]
46365                     };
46366                     
46367                     if(this.labelWidth > 12){
46368                         label.style = "width: " + this.labelWidth + 'px';
46369                     }
46370                     if(this.labelWidth < 13 && this.labelmd == 0){
46371                         this.labelmd = this.labelWidth;
46372                     }
46373                     if(this.labellg > 0){
46374                         label.cls += ' col-lg-' + this.labellg;
46375                         input.cls += ' col-lg-' + (12 - this.labellg);
46376                     }
46377                     if(this.labelmd > 0){
46378                         label.cls += ' col-md-' + this.labelmd;
46379                         container.cls += ' col-md-' + (12 - this.labelmd);
46380                     }
46381                     if(this.labelsm > 0){
46382                         label.cls += ' col-sm-' + this.labelsm;
46383                         container.cls += ' col-sm-' + (12 - this.labelsm);
46384                     }
46385                     if(this.labelxs > 0){
46386                         label.cls += ' col-xs-' + this.labelxs;
46387                         container.cls += ' col-xs-' + (12 - this.labelxs);
46388                     }
46389                 }
46390             }
46391             
46392             cfg.cn = [
46393                 label,
46394                 container
46395             ];
46396             
46397             var settings = this;
46398             
46399             ['xs','sm','md','lg'].map(function(size){
46400                 if (settings[size]) {
46401                     cfg.cls += ' col-' + size + '-' + settings[size];
46402                 }
46403             });
46404             
46405             this.store = new Roo.data.Store({
46406                 proxy : new Roo.data.MemoryProxy({}),
46407                 reader : new Roo.data.JsonReader({
46408                     fields : [
46409                         {
46410                             'name' : 'name',
46411                             'type' : 'string'
46412                         },
46413                         {
46414                             'name' : 'iso2',
46415                             'type' : 'string'
46416                         },
46417                         {
46418                             'name' : 'dialCode',
46419                             'type' : 'string'
46420                         },
46421                         {
46422                             'name' : 'priority',
46423                             'type' : 'string'
46424                         },
46425                         {
46426                             'name' : 'areaCodes',
46427                             'type' : 'string'
46428                         }
46429                     ]
46430                 })
46431             });
46432             
46433             if(!this.preferedCountries) {
46434                 this.preferedCountries = [
46435                     'hk',
46436                     'gb',
46437                     'us'
46438                 ];
46439             }
46440             
46441             var p = this.preferedCountries.reverse();
46442             
46443             if(p) {
46444                 for (var i = 0; i < p.length; i++) {
46445                     for (var j = 0; j < this.allCountries.length; j++) {
46446                         if(this.allCountries[j].iso2 == p[i]) {
46447                             var t = this.allCountries[j];
46448                             this.allCountries.splice(j,1);
46449                             this.allCountries.unshift(t);
46450                         }
46451                     } 
46452                 }
46453             }
46454             
46455             this.store.proxy.data = {
46456                 success: true,
46457                 data: this.allCountries
46458             };
46459             
46460             return cfg;
46461         },
46462         
46463         initEvents : function()
46464         {
46465             this.createList();
46466             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
46467             
46468             this.indicator = this.indicatorEl();
46469             this.flag = this.flagEl();
46470             this.dialCodeHolder = this.dialCodeHolderEl();
46471             
46472             this.trigger = this.el.select('div.flag-box',true).first();
46473             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
46474             
46475             var _this = this;
46476             
46477             (function(){
46478                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46479                 _this.list.setWidth(lw);
46480             }).defer(100);
46481             
46482             this.list.on('mouseover', this.onViewOver, this);
46483             this.list.on('mousemove', this.onViewMove, this);
46484             this.inputEl().on("keyup", this.onKeyUp, this);
46485             this.inputEl().on("keypress", this.onKeyPress, this);
46486             
46487             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
46488
46489             this.view = new Roo.View(this.list, this.tpl, {
46490                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
46491             });
46492             
46493             this.view.on('click', this.onViewClick, this);
46494             this.setValue(this.defaultDialCode);
46495         },
46496         
46497         onTriggerClick : function(e)
46498         {
46499             Roo.log('trigger click');
46500             if(this.disabled){
46501                 return;
46502             }
46503             
46504             if(this.isExpanded()){
46505                 this.collapse();
46506                 this.hasFocus = false;
46507             }else {
46508                 this.store.load({});
46509                 this.hasFocus = true;
46510                 this.expand();
46511             }
46512         },
46513         
46514         isExpanded : function()
46515         {
46516             return this.list.isVisible();
46517         },
46518         
46519         collapse : function()
46520         {
46521             if(!this.isExpanded()){
46522                 return;
46523             }
46524             this.list.hide();
46525             Roo.get(document).un('mousedown', this.collapseIf, this);
46526             Roo.get(document).un('mousewheel', this.collapseIf, this);
46527             this.fireEvent('collapse', this);
46528             this.validate();
46529         },
46530         
46531         expand : function()
46532         {
46533             Roo.log('expand');
46534
46535             if(this.isExpanded() || !this.hasFocus){
46536                 return;
46537             }
46538             
46539             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
46540             this.list.setWidth(lw);
46541             
46542             this.list.show();
46543             this.restrictHeight();
46544             
46545             Roo.get(document).on('mousedown', this.collapseIf, this);
46546             Roo.get(document).on('mousewheel', this.collapseIf, this);
46547             
46548             this.fireEvent('expand', this);
46549         },
46550         
46551         restrictHeight : function()
46552         {
46553             this.list.alignTo(this.inputEl(), this.listAlign);
46554             this.list.alignTo(this.inputEl(), this.listAlign);
46555         },
46556         
46557         onViewOver : function(e, t)
46558         {
46559             if(this.inKeyMode){
46560                 return;
46561             }
46562             var item = this.view.findItemFromChild(t);
46563             
46564             if(item){
46565                 var index = this.view.indexOf(item);
46566                 this.select(index, false);
46567             }
46568         },
46569
46570         // private
46571         onViewClick : function(view, doFocus, el, e)
46572         {
46573             var index = this.view.getSelectedIndexes()[0];
46574             
46575             var r = this.store.getAt(index);
46576             
46577             if(r){
46578                 this.onSelect(r, index);
46579             }
46580             if(doFocus !== false && !this.blockFocus){
46581                 this.inputEl().focus();
46582             }
46583         },
46584         
46585         onViewMove : function(e, t)
46586         {
46587             this.inKeyMode = false;
46588         },
46589         
46590         select : function(index, scrollIntoView)
46591         {
46592             this.selectedIndex = index;
46593             this.view.select(index);
46594             if(scrollIntoView !== false){
46595                 var el = this.view.getNode(index);
46596                 if(el){
46597                     this.list.scrollChildIntoView(el, false);
46598                 }
46599             }
46600         },
46601         
46602         createList : function()
46603         {
46604             this.list = Roo.get(document.body).createChild({
46605                 tag: 'ul',
46606                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
46607                 style: 'display:none'
46608             });
46609             
46610             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
46611         },
46612         
46613         collapseIf : function(e)
46614         {
46615             var in_combo  = e.within(this.el);
46616             var in_list =  e.within(this.list);
46617             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
46618             
46619             if (in_combo || in_list || is_list) {
46620                 return;
46621             }
46622             this.collapse();
46623         },
46624         
46625         onSelect : function(record, index)
46626         {
46627             if(this.fireEvent('beforeselect', this, record, index) !== false){
46628                 
46629                 this.setFlagClass(record.data.iso2);
46630                 this.setDialCode(record.data.dialCode);
46631                 this.hasFocus = false;
46632                 this.collapse();
46633                 this.fireEvent('select', this, record, index);
46634             }
46635         },
46636         
46637         flagEl : function()
46638         {
46639             var flag = this.el.select('div.flag',true).first();
46640             if(!flag){
46641                 return false;
46642             }
46643             return flag;
46644         },
46645         
46646         dialCodeHolderEl : function()
46647         {
46648             var d = this.el.select('input.dial-code-holder',true).first();
46649             if(!d){
46650                 return false;
46651             }
46652             return d;
46653         },
46654         
46655         setDialCode : function(v)
46656         {
46657             this.dialCodeHolder.dom.value = '+'+v;
46658         },
46659         
46660         setFlagClass : function(n)
46661         {
46662             this.flag.dom.className = 'flag '+n;
46663         },
46664         
46665         getValue : function()
46666         {
46667             var v = this.inputEl().getValue();
46668             if(this.dialCodeHolder) {
46669                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
46670             }
46671             return v;
46672         },
46673         
46674         setValue : function(v)
46675         {
46676             var d = this.getDialCode(v);
46677             
46678             //invalid dial code
46679             if(v.length == 0 || !d || d.length == 0) {
46680                 if(this.rendered){
46681                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
46682                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
46683                 }
46684                 return;
46685             }
46686             
46687             //valid dial code
46688             this.setFlagClass(this.dialCodeMapping[d].iso2);
46689             this.setDialCode(d);
46690             this.inputEl().dom.value = v.replace('+'+d,'');
46691             this.hiddenEl().dom.value = this.getValue();
46692             
46693             this.validate();
46694         },
46695         
46696         getDialCode : function(v)
46697         {
46698             v = v ||  '';
46699             
46700             if (v.length == 0) {
46701                 return this.dialCodeHolder.dom.value;
46702             }
46703             
46704             var dialCode = "";
46705             if (v.charAt(0) != "+") {
46706                 return false;
46707             }
46708             var numericChars = "";
46709             for (var i = 1; i < v.length; i++) {
46710               var c = v.charAt(i);
46711               if (!isNaN(c)) {
46712                 numericChars += c;
46713                 if (this.dialCodeMapping[numericChars]) {
46714                   dialCode = v.substr(1, i);
46715                 }
46716                 if (numericChars.length == 4) {
46717                   break;
46718                 }
46719               }
46720             }
46721             return dialCode;
46722         },
46723         
46724         reset : function()
46725         {
46726             this.setValue(this.defaultDialCode);
46727             this.validate();
46728         },
46729         
46730         hiddenEl : function()
46731         {
46732             return this.el.select('input.hidden-tel-input',true).first();
46733         },
46734         
46735         // after setting val
46736         onKeyUp : function(e){
46737             this.setValue(this.getValue());
46738         },
46739         
46740         onKeyPress : function(e){
46741             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
46742                 e.stopEvent();
46743             }
46744         }
46745         
46746 });
46747 /**
46748  * @class Roo.bootstrap.form.MoneyField
46749  * @extends Roo.bootstrap.form.ComboBox
46750  * Bootstrap MoneyField class
46751  * 
46752  * @constructor
46753  * Create a new MoneyField.
46754  * @param {Object} config Configuration options
46755  */
46756
46757 Roo.bootstrap.form.MoneyField = function(config) {
46758     
46759     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
46760     
46761 };
46762
46763 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
46764     
46765     /**
46766      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
46767      */
46768     allowDecimals : true,
46769     /**
46770      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
46771      */
46772     decimalSeparator : ".",
46773     /**
46774      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
46775      */
46776     decimalPrecision : 0,
46777     /**
46778      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
46779      */
46780     allowNegative : true,
46781     /**
46782      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
46783      */
46784     allowZero: true,
46785     /**
46786      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
46787      */
46788     minValue : Number.NEGATIVE_INFINITY,
46789     /**
46790      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
46791      */
46792     maxValue : Number.MAX_VALUE,
46793     /**
46794      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
46795      */
46796     minText : "The minimum value for this field is {0}",
46797     /**
46798      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
46799      */
46800     maxText : "The maximum value for this field is {0}",
46801     /**
46802      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
46803      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
46804      */
46805     nanText : "{0} is not a valid number",
46806     /**
46807      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
46808      */
46809     castInt : true,
46810     /**
46811      * @cfg {String} defaults currency of the MoneyField
46812      * value should be in lkey
46813      */
46814     defaultCurrency : false,
46815     /**
46816      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
46817      */
46818     thousandsDelimiter : false,
46819     /**
46820      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
46821      */
46822     max_length: false,
46823     
46824     inputlg : 9,
46825     inputmd : 9,
46826     inputsm : 9,
46827     inputxs : 6,
46828      /**
46829      * @cfg {Roo.data.Store} store  Store to lookup currency??
46830      */
46831     store : false,
46832     
46833     getAutoCreate : function()
46834     {
46835         var align = this.labelAlign || this.parentLabelAlign();
46836         
46837         var id = Roo.id();
46838
46839         var cfg = {
46840             cls: 'form-group',
46841             cn: []
46842         };
46843
46844         var input =  {
46845             tag: 'input',
46846             id : id,
46847             cls : 'form-control roo-money-amount-input',
46848             autocomplete: 'new-password'
46849         };
46850         
46851         var hiddenInput = {
46852             tag: 'input',
46853             type: 'hidden',
46854             id: Roo.id(),
46855             cls: 'hidden-number-input'
46856         };
46857         
46858         if(this.max_length) {
46859             input.maxlength = this.max_length; 
46860         }
46861         
46862         if (this.name) {
46863             hiddenInput.name = this.name;
46864         }
46865
46866         if (this.disabled) {
46867             input.disabled = true;
46868         }
46869
46870         var clg = 12 - this.inputlg;
46871         var cmd = 12 - this.inputmd;
46872         var csm = 12 - this.inputsm;
46873         var cxs = 12 - this.inputxs;
46874         
46875         var container = {
46876             tag : 'div',
46877             cls : 'row roo-money-field',
46878             cn : [
46879                 {
46880                     tag : 'div',
46881                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
46882                     cn : [
46883                         {
46884                             tag : 'div',
46885                             cls: 'roo-select2-container input-group',
46886                             cn: [
46887                                 {
46888                                     tag : 'input',
46889                                     cls : 'form-control roo-money-currency-input',
46890                                     autocomplete: 'new-password',
46891                                     readOnly : 1,
46892                                     name : this.currencyName
46893                                 },
46894                                 {
46895                                     tag :'span',
46896                                     cls : 'input-group-addon',
46897                                     cn : [
46898                                         {
46899                                             tag: 'span',
46900                                             cls: 'caret'
46901                                         }
46902                                     ]
46903                                 }
46904                             ]
46905                         }
46906                     ]
46907                 },
46908                 {
46909                     tag : 'div',
46910                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
46911                     cn : [
46912                         {
46913                             tag: 'div',
46914                             cls: this.hasFeedback ? 'has-feedback' : '',
46915                             cn: [
46916                                 input
46917                             ]
46918                         }
46919                     ]
46920                 }
46921             ]
46922             
46923         };
46924         
46925         if (this.fieldLabel.length) {
46926             var indicator = {
46927                 tag: 'i',
46928                 tooltip: 'This field is required'
46929             };
46930
46931             var label = {
46932                 tag: 'label',
46933                 'for':  id,
46934                 cls: 'control-label',
46935                 cn: []
46936             };
46937
46938             var label_text = {
46939                 tag: 'span',
46940                 html: this.fieldLabel
46941             };
46942
46943             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46944             label.cn = [
46945                 indicator,
46946                 label_text
46947             ];
46948
46949             if(this.indicatorpos == 'right') {
46950                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46951                 label.cn = [
46952                     label_text,
46953                     indicator
46954                 ];
46955             }
46956
46957             if(align == 'left') {
46958                 container = {
46959                     tag: 'div',
46960                     cn: [
46961                         container
46962                     ]
46963                 };
46964
46965                 if(this.labelWidth > 12){
46966                     label.style = "width: " + this.labelWidth + 'px';
46967                 }
46968                 if(this.labelWidth < 13 && this.labelmd == 0){
46969                     this.labelmd = this.labelWidth;
46970                 }
46971                 if(this.labellg > 0){
46972                     label.cls += ' col-lg-' + this.labellg;
46973                     input.cls += ' col-lg-' + (12 - this.labellg);
46974                 }
46975                 if(this.labelmd > 0){
46976                     label.cls += ' col-md-' + this.labelmd;
46977                     container.cls += ' col-md-' + (12 - this.labelmd);
46978                 }
46979                 if(this.labelsm > 0){
46980                     label.cls += ' col-sm-' + this.labelsm;
46981                     container.cls += ' col-sm-' + (12 - this.labelsm);
46982                 }
46983                 if(this.labelxs > 0){
46984                     label.cls += ' col-xs-' + this.labelxs;
46985                     container.cls += ' col-xs-' + (12 - this.labelxs);
46986                 }
46987             }
46988         }
46989
46990         cfg.cn = [
46991             label,
46992             container,
46993             hiddenInput
46994         ];
46995         
46996         var settings = this;
46997
46998         ['xs','sm','md','lg'].map(function(size){
46999             if (settings[size]) {
47000                 cfg.cls += ' col-' + size + '-' + settings[size];
47001             }
47002         });
47003         
47004         return cfg;
47005     },
47006     
47007     initEvents : function()
47008     {
47009         this.indicator = this.indicatorEl();
47010         
47011         this.initCurrencyEvent();
47012         
47013         this.initNumberEvent();
47014     },
47015     
47016     initCurrencyEvent : function()
47017     {
47018         if (!this.store) {
47019             throw "can not find store for combo";
47020         }
47021         
47022         this.store = Roo.factory(this.store, Roo.data);
47023         this.store.parent = this;
47024         
47025         this.createList();
47026         
47027         this.triggerEl = this.el.select('.input-group-addon', true).first();
47028         
47029         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
47030         
47031         var _this = this;
47032         
47033         (function(){
47034             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47035             _this.list.setWidth(lw);
47036         }).defer(100);
47037         
47038         this.list.on('mouseover', this.onViewOver, this);
47039         this.list.on('mousemove', this.onViewMove, this);
47040         this.list.on('scroll', this.onViewScroll, this);
47041         
47042         if(!this.tpl){
47043             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
47044         }
47045         
47046         this.view = new Roo.View(this.list, this.tpl, {
47047             singleSelect:true, store: this.store, selectedClass: this.selectedClass
47048         });
47049         
47050         this.view.on('click', this.onViewClick, this);
47051         
47052         this.store.on('beforeload', this.onBeforeLoad, this);
47053         this.store.on('load', this.onLoad, this);
47054         this.store.on('loadexception', this.onLoadException, this);
47055         
47056         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
47057             "up" : function(e){
47058                 this.inKeyMode = true;
47059                 this.selectPrev();
47060             },
47061
47062             "down" : function(e){
47063                 if(!this.isExpanded()){
47064                     this.onTriggerClick();
47065                 }else{
47066                     this.inKeyMode = true;
47067                     this.selectNext();
47068                 }
47069             },
47070
47071             "enter" : function(e){
47072                 this.collapse();
47073                 
47074                 if(this.fireEvent("specialkey", this, e)){
47075                     this.onViewClick(false);
47076                 }
47077                 
47078                 return true;
47079             },
47080
47081             "esc" : function(e){
47082                 this.collapse();
47083             },
47084
47085             "tab" : function(e){
47086                 this.collapse();
47087                 
47088                 if(this.fireEvent("specialkey", this, e)){
47089                     this.onViewClick(false);
47090                 }
47091                 
47092                 return true;
47093             },
47094
47095             scope : this,
47096
47097             doRelay : function(foo, bar, hname){
47098                 if(hname == 'down' || this.scope.isExpanded()){
47099                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
47100                 }
47101                 return true;
47102             },
47103
47104             forceKeyDown: true
47105         });
47106         
47107         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
47108         
47109     },
47110     
47111     initNumberEvent : function(e)
47112     {
47113         this.inputEl().on("keydown" , this.fireKey,  this);
47114         this.inputEl().on("focus", this.onFocus,  this);
47115         this.inputEl().on("blur", this.onBlur,  this);
47116         
47117         this.inputEl().relayEvent('keyup', this);
47118         
47119         if(this.indicator){
47120             this.indicator.addClass('invisible');
47121         }
47122  
47123         this.originalValue = this.getValue();
47124         
47125         if(this.validationEvent == 'keyup'){
47126             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
47127             this.inputEl().on('keyup', this.filterValidation, this);
47128         }
47129         else if(this.validationEvent !== false){
47130             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
47131         }
47132         
47133         if(this.selectOnFocus){
47134             this.on("focus", this.preFocus, this);
47135             
47136         }
47137         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
47138             this.inputEl().on("keypress", this.filterKeys, this);
47139         } else {
47140             this.inputEl().relayEvent('keypress', this);
47141         }
47142         
47143         var allowed = "0123456789";
47144         
47145         if(this.allowDecimals){
47146             allowed += this.decimalSeparator;
47147         }
47148         
47149         if(this.allowNegative){
47150             allowed += "-";
47151         }
47152         
47153         if(this.thousandsDelimiter) {
47154             allowed += ",";
47155         }
47156         
47157         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
47158         
47159         var keyPress = function(e){
47160             
47161             var k = e.getKey();
47162             
47163             var c = e.getCharCode();
47164             
47165             if(
47166                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
47167                     allowed.indexOf(String.fromCharCode(c)) === -1
47168             ){
47169                 e.stopEvent();
47170                 return;
47171             }
47172             
47173             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
47174                 return;
47175             }
47176             
47177             if(allowed.indexOf(String.fromCharCode(c)) === -1){
47178                 e.stopEvent();
47179             }
47180         };
47181         
47182         this.inputEl().on("keypress", keyPress, this);
47183         
47184     },
47185     
47186     onTriggerClick : function(e)
47187     {   
47188         if(this.disabled){
47189             return;
47190         }
47191         
47192         this.page = 0;
47193         this.loadNext = false;
47194         
47195         if(this.isExpanded()){
47196             this.collapse();
47197             return;
47198         }
47199         
47200         this.hasFocus = true;
47201         
47202         if(this.triggerAction == 'all') {
47203             this.doQuery(this.allQuery, true);
47204             return;
47205         }
47206         
47207         this.doQuery(this.getRawValue());
47208     },
47209     
47210     getCurrency : function()
47211     {   
47212         var v = this.currencyEl().getValue();
47213         
47214         return v;
47215     },
47216     
47217     restrictHeight : function()
47218     {
47219         this.list.alignTo(this.currencyEl(), this.listAlign);
47220         this.list.alignTo(this.currencyEl(), this.listAlign);
47221     },
47222     
47223     onViewClick : function(view, doFocus, el, e)
47224     {
47225         var index = this.view.getSelectedIndexes()[0];
47226         
47227         var r = this.store.getAt(index);
47228         
47229         if(r){
47230             this.onSelect(r, index);
47231         }
47232     },
47233     
47234     onSelect : function(record, index){
47235         
47236         if(this.fireEvent('beforeselect', this, record, index) !== false){
47237         
47238             this.setFromCurrencyData(index > -1 ? record.data : false);
47239             
47240             this.collapse();
47241             
47242             this.fireEvent('select', this, record, index);
47243         }
47244     },
47245     
47246     setFromCurrencyData : function(o)
47247     {
47248         var currency = '';
47249         
47250         this.lastCurrency = o;
47251         
47252         if (this.currencyField) {
47253             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
47254         } else {
47255             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
47256         }
47257         
47258         this.lastSelectionText = currency;
47259         
47260         //setting default currency
47261         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
47262             this.setCurrency(this.defaultCurrency);
47263             return;
47264         }
47265         
47266         this.setCurrency(currency);
47267     },
47268     
47269     setFromData : function(o)
47270     {
47271         var c = {};
47272         
47273         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
47274         
47275         this.setFromCurrencyData(c);
47276         
47277         var value = '';
47278         
47279         if (this.name) {
47280             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
47281         } else {
47282             Roo.log('no value set for '+ (this.name ? this.name : this.id));
47283         }
47284         
47285         this.setValue(value);
47286         
47287     },
47288     
47289     setCurrency : function(v)
47290     {   
47291         this.currencyValue = v;
47292         
47293         if(this.rendered){
47294             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
47295             this.validate();
47296         }
47297     },
47298     
47299     setValue : function(v)
47300     {
47301         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
47302         
47303         this.value = v;
47304         
47305         if(this.rendered){
47306             
47307             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47308             
47309             this.inputEl().dom.value = (v == '') ? '' :
47310                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
47311             
47312             if(!this.allowZero && v === '0') {
47313                 this.hiddenEl().dom.value = '';
47314                 this.inputEl().dom.value = '';
47315             }
47316             
47317             this.validate();
47318         }
47319     },
47320     
47321     getRawValue : function()
47322     {
47323         var v = this.inputEl().getValue();
47324         
47325         return v;
47326     },
47327     
47328     getValue : function()
47329     {
47330         return this.fixPrecision(this.parseValue(this.getRawValue()));
47331     },
47332     
47333     parseValue : function(value)
47334     {
47335         if(this.thousandsDelimiter) {
47336             value += "";
47337             r = new RegExp(",", "g");
47338             value = value.replace(r, "");
47339         }
47340         
47341         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
47342         return isNaN(value) ? '' : value;
47343         
47344     },
47345     
47346     fixPrecision : function(value)
47347     {
47348         if(this.thousandsDelimiter) {
47349             value += "";
47350             r = new RegExp(",", "g");
47351             value = value.replace(r, "");
47352         }
47353         
47354         var nan = isNaN(value);
47355         
47356         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
47357             return nan ? '' : value;
47358         }
47359         return parseFloat(value).toFixed(this.decimalPrecision);
47360     },
47361     
47362     decimalPrecisionFcn : function(v)
47363     {
47364         return Math.floor(v);
47365     },
47366     
47367     validateValue : function(value)
47368     {
47369         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
47370             return false;
47371         }
47372         
47373         var num = this.parseValue(value);
47374         
47375         if(isNaN(num)){
47376             this.markInvalid(String.format(this.nanText, value));
47377             return false;
47378         }
47379         
47380         if(num < this.minValue){
47381             this.markInvalid(String.format(this.minText, this.minValue));
47382             return false;
47383         }
47384         
47385         if(num > this.maxValue){
47386             this.markInvalid(String.format(this.maxText, this.maxValue));
47387             return false;
47388         }
47389         
47390         return true;
47391     },
47392     
47393     validate : function()
47394     {
47395         if(this.disabled || this.allowBlank){
47396             this.markValid();
47397             return true;
47398         }
47399         
47400         var currency = this.getCurrency();
47401         
47402         if(this.validateValue(this.getRawValue()) && currency.length){
47403             this.markValid();
47404             return true;
47405         }
47406         
47407         this.markInvalid();
47408         return false;
47409     },
47410     
47411     getName: function()
47412     {
47413         return this.name;
47414     },
47415     
47416     beforeBlur : function()
47417     {
47418         if(!this.castInt){
47419             return;
47420         }
47421         
47422         var v = this.parseValue(this.getRawValue());
47423         
47424         if(v || v == 0){
47425             this.setValue(v);
47426         }
47427     },
47428     
47429     onBlur : function()
47430     {
47431         this.beforeBlur();
47432         
47433         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
47434             //this.el.removeClass(this.focusClass);
47435         }
47436         
47437         this.hasFocus = false;
47438         
47439         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
47440             this.validate();
47441         }
47442         
47443         var v = this.getValue();
47444         
47445         if(String(v) !== String(this.startValue)){
47446             this.fireEvent('change', this, v, this.startValue);
47447         }
47448         
47449         this.fireEvent("blur", this);
47450     },
47451     
47452     inputEl : function()
47453     {
47454         return this.el.select('.roo-money-amount-input', true).first();
47455     },
47456     
47457     currencyEl : function()
47458     {
47459         return this.el.select('.roo-money-currency-input', true).first();
47460     },
47461     
47462     hiddenEl : function()
47463     {
47464         return this.el.select('input.hidden-number-input',true).first();
47465     }
47466     
47467 });/**
47468  * @class Roo.bootstrap.BezierSignature
47469  * @extends Roo.bootstrap.Component
47470  * Bootstrap BezierSignature class
47471  * This script refer to:
47472  *    Title: Signature Pad
47473  *    Author: szimek
47474  *    Availability: https://github.com/szimek/signature_pad
47475  *
47476  * @constructor
47477  * Create a new BezierSignature
47478  * @param {Object} config The config object
47479  */
47480
47481 Roo.bootstrap.BezierSignature = function(config){
47482     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
47483     this.addEvents({
47484         "resize" : true
47485     });
47486 };
47487
47488 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
47489 {
47490      
47491     curve_data: [],
47492     
47493     is_empty: true,
47494     
47495     mouse_btn_down: true,
47496     
47497     /**
47498      * @cfg {int} canvas height
47499      */
47500     canvas_height: '200px',
47501     
47502     /**
47503      * @cfg {float|function} Radius of a single dot.
47504      */ 
47505     dot_size: false,
47506     
47507     /**
47508      * @cfg {float} Minimum width of a line. Defaults to 0.5.
47509      */
47510     min_width: 0.5,
47511     
47512     /**
47513      * @cfg {float} Maximum width of a line. Defaults to 2.5.
47514      */
47515     max_width: 2.5,
47516     
47517     /**
47518      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
47519      */
47520     throttle: 16,
47521     
47522     /**
47523      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
47524      */
47525     min_distance: 5,
47526     
47527     /**
47528      * @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.
47529      */
47530     bg_color: 'rgba(0, 0, 0, 0)',
47531     
47532     /**
47533      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
47534      */
47535     dot_color: 'black',
47536     
47537     /**
47538      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
47539      */ 
47540     velocity_filter_weight: 0.7,
47541     
47542     /**
47543      * @cfg {function} Callback when stroke begin. 
47544      */
47545     onBegin: false,
47546     
47547     /**
47548      * @cfg {function} Callback when stroke end.
47549      */
47550     onEnd: false,
47551     
47552     getAutoCreate : function()
47553     {
47554         var cls = 'roo-signature column';
47555         
47556         if(this.cls){
47557             cls += ' ' + this.cls;
47558         }
47559         
47560         var col_sizes = [
47561             'lg',
47562             'md',
47563             'sm',
47564             'xs'
47565         ];
47566         
47567         for(var i = 0; i < col_sizes.length; i++) {
47568             if(this[col_sizes[i]]) {
47569                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
47570             }
47571         }
47572         
47573         var cfg = {
47574             tag: 'div',
47575             cls: cls,
47576             cn: [
47577                 {
47578                     tag: 'div',
47579                     cls: 'roo-signature-body',
47580                     cn: [
47581                         {
47582                             tag: 'canvas',
47583                             cls: 'roo-signature-body-canvas',
47584                             height: this.canvas_height,
47585                             width: this.canvas_width
47586                         }
47587                     ]
47588                 },
47589                 {
47590                     tag: 'input',
47591                     type: 'file',
47592                     style: 'display: none'
47593                 }
47594             ]
47595         };
47596         
47597         return cfg;
47598     },
47599     
47600     initEvents: function() 
47601     {
47602         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
47603         
47604         var canvas = this.canvasEl();
47605         
47606         // mouse && touch event swapping...
47607         canvas.dom.style.touchAction = 'none';
47608         canvas.dom.style.msTouchAction = 'none';
47609         
47610         this.mouse_btn_down = false;
47611         canvas.on('mousedown', this._handleMouseDown, this);
47612         canvas.on('mousemove', this._handleMouseMove, this);
47613         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
47614         
47615         if (window.PointerEvent) {
47616             canvas.on('pointerdown', this._handleMouseDown, this);
47617             canvas.on('pointermove', this._handleMouseMove, this);
47618             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
47619         }
47620         
47621         if ('ontouchstart' in window) {
47622             canvas.on('touchstart', this._handleTouchStart, this);
47623             canvas.on('touchmove', this._handleTouchMove, this);
47624             canvas.on('touchend', this._handleTouchEnd, this);
47625         }
47626         
47627         Roo.EventManager.onWindowResize(this.resize, this, true);
47628         
47629         // file input event
47630         this.fileEl().on('change', this.uploadImage, this);
47631         
47632         this.clear();
47633         
47634         this.resize();
47635     },
47636     
47637     resize: function(){
47638         
47639         var canvas = this.canvasEl().dom;
47640         var ctx = this.canvasElCtx();
47641         var img_data = false;
47642         
47643         if(canvas.width > 0) {
47644             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
47645         }
47646         // setting canvas width will clean img data
47647         canvas.width = 0;
47648         
47649         var style = window.getComputedStyle ? 
47650             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
47651             
47652         var padding_left = parseInt(style.paddingLeft) || 0;
47653         var padding_right = parseInt(style.paddingRight) || 0;
47654         
47655         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
47656         
47657         if(img_data) {
47658             ctx.putImageData(img_data, 0, 0);
47659         }
47660     },
47661     
47662     _handleMouseDown: function(e)
47663     {
47664         if (e.browserEvent.which === 1) {
47665             this.mouse_btn_down = true;
47666             this.strokeBegin(e);
47667         }
47668     },
47669     
47670     _handleMouseMove: function (e)
47671     {
47672         if (this.mouse_btn_down) {
47673             this.strokeMoveUpdate(e);
47674         }
47675     },
47676     
47677     _handleMouseUp: function (e)
47678     {
47679         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
47680             this.mouse_btn_down = false;
47681             this.strokeEnd(e);
47682         }
47683     },
47684     
47685     _handleTouchStart: function (e) {
47686         
47687         e.preventDefault();
47688         if (e.browserEvent.targetTouches.length === 1) {
47689             // var touch = e.browserEvent.changedTouches[0];
47690             // this.strokeBegin(touch);
47691             
47692              this.strokeBegin(e); // assume e catching the correct xy...
47693         }
47694     },
47695     
47696     _handleTouchMove: function (e) {
47697         e.preventDefault();
47698         // var touch = event.targetTouches[0];
47699         // _this._strokeMoveUpdate(touch);
47700         this.strokeMoveUpdate(e);
47701     },
47702     
47703     _handleTouchEnd: function (e) {
47704         var wasCanvasTouched = e.target === this.canvasEl().dom;
47705         if (wasCanvasTouched) {
47706             e.preventDefault();
47707             // var touch = event.changedTouches[0];
47708             // _this._strokeEnd(touch);
47709             this.strokeEnd(e);
47710         }
47711     },
47712     
47713     reset: function () {
47714         this._lastPoints = [];
47715         this._lastVelocity = 0;
47716         this._lastWidth = (this.min_width + this.max_width) / 2;
47717         this.canvasElCtx().fillStyle = this.dot_color;
47718     },
47719     
47720     strokeMoveUpdate: function(e)
47721     {
47722         this.strokeUpdate(e);
47723         
47724         if (this.throttle) {
47725             this.throttleStroke(this.strokeUpdate, this.throttle);
47726         }
47727         else {
47728             this.strokeUpdate(e);
47729         }
47730     },
47731     
47732     strokeBegin: function(e)
47733     {
47734         var newPointGroup = {
47735             color: this.dot_color,
47736             points: []
47737         };
47738         
47739         if (typeof this.onBegin === 'function') {
47740             this.onBegin(e);
47741         }
47742         
47743         this.curve_data.push(newPointGroup);
47744         this.reset();
47745         this.strokeUpdate(e);
47746     },
47747     
47748     strokeUpdate: function(e)
47749     {
47750         var rect = this.canvasEl().dom.getBoundingClientRect();
47751         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
47752         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
47753         var lastPoints = lastPointGroup.points;
47754         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
47755         var isLastPointTooClose = lastPoint
47756             ? point.distanceTo(lastPoint) <= this.min_distance
47757             : false;
47758         var color = lastPointGroup.color;
47759         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
47760             var curve = this.addPoint(point);
47761             if (!lastPoint) {
47762                 this.drawDot({color: color, point: point});
47763             }
47764             else if (curve) {
47765                 this.drawCurve({color: color, curve: curve});
47766             }
47767             lastPoints.push({
47768                 time: point.time,
47769                 x: point.x,
47770                 y: point.y
47771             });
47772         }
47773     },
47774     
47775     strokeEnd: function(e)
47776     {
47777         this.strokeUpdate(e);
47778         if (typeof this.onEnd === 'function') {
47779             this.onEnd(e);
47780         }
47781     },
47782     
47783     addPoint:  function (point) {
47784         var _lastPoints = this._lastPoints;
47785         _lastPoints.push(point);
47786         if (_lastPoints.length > 2) {
47787             if (_lastPoints.length === 3) {
47788                 _lastPoints.unshift(_lastPoints[0]);
47789             }
47790             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
47791             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
47792             _lastPoints.shift();
47793             return curve;
47794         }
47795         return null;
47796     },
47797     
47798     calculateCurveWidths: function (startPoint, endPoint) {
47799         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
47800             (1 - this.velocity_filter_weight) * this._lastVelocity;
47801
47802         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
47803         var widths = {
47804             end: newWidth,
47805             start: this._lastWidth
47806         };
47807         
47808         this._lastVelocity = velocity;
47809         this._lastWidth = newWidth;
47810         return widths;
47811     },
47812     
47813     drawDot: function (_a) {
47814         var color = _a.color, point = _a.point;
47815         var ctx = this.canvasElCtx();
47816         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
47817         ctx.beginPath();
47818         this.drawCurveSegment(point.x, point.y, width);
47819         ctx.closePath();
47820         ctx.fillStyle = color;
47821         ctx.fill();
47822     },
47823     
47824     drawCurve: function (_a) {
47825         var color = _a.color, curve = _a.curve;
47826         var ctx = this.canvasElCtx();
47827         var widthDelta = curve.endWidth - curve.startWidth;
47828         var drawSteps = Math.floor(curve.length()) * 2;
47829         ctx.beginPath();
47830         ctx.fillStyle = color;
47831         for (var i = 0; i < drawSteps; i += 1) {
47832         var t = i / drawSteps;
47833         var tt = t * t;
47834         var ttt = tt * t;
47835         var u = 1 - t;
47836         var uu = u * u;
47837         var uuu = uu * u;
47838         var x = uuu * curve.startPoint.x;
47839         x += 3 * uu * t * curve.control1.x;
47840         x += 3 * u * tt * curve.control2.x;
47841         x += ttt * curve.endPoint.x;
47842         var y = uuu * curve.startPoint.y;
47843         y += 3 * uu * t * curve.control1.y;
47844         y += 3 * u * tt * curve.control2.y;
47845         y += ttt * curve.endPoint.y;
47846         var width = curve.startWidth + ttt * widthDelta;
47847         this.drawCurveSegment(x, y, width);
47848         }
47849         ctx.closePath();
47850         ctx.fill();
47851     },
47852     
47853     drawCurveSegment: function (x, y, width) {
47854         var ctx = this.canvasElCtx();
47855         ctx.moveTo(x, y);
47856         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
47857         this.is_empty = false;
47858     },
47859     
47860     clear: function()
47861     {
47862         var ctx = this.canvasElCtx();
47863         var canvas = this.canvasEl().dom;
47864         ctx.fillStyle = this.bg_color;
47865         ctx.clearRect(0, 0, canvas.width, canvas.height);
47866         ctx.fillRect(0, 0, canvas.width, canvas.height);
47867         this.curve_data = [];
47868         this.reset();
47869         this.is_empty = true;
47870     },
47871     
47872     fileEl: function()
47873     {
47874         return  this.el.select('input',true).first();
47875     },
47876     
47877     canvasEl: function()
47878     {
47879         return this.el.select('canvas',true).first();
47880     },
47881     
47882     canvasElCtx: function()
47883     {
47884         return this.el.select('canvas',true).first().dom.getContext('2d');
47885     },
47886     
47887     getImage: function(type)
47888     {
47889         if(this.is_empty) {
47890             return false;
47891         }
47892         
47893         // encryption ?
47894         return this.canvasEl().dom.toDataURL('image/'+type, 1);
47895     },
47896     
47897     drawFromImage: function(img_src)
47898     {
47899         var img = new Image();
47900         
47901         img.onload = function(){
47902             this.canvasElCtx().drawImage(img, 0, 0);
47903         }.bind(this);
47904         
47905         img.src = img_src;
47906         
47907         this.is_empty = false;
47908     },
47909     
47910     selectImage: function()
47911     {
47912         this.fileEl().dom.click();
47913     },
47914     
47915     uploadImage: function(e)
47916     {
47917         var reader = new FileReader();
47918         
47919         reader.onload = function(e){
47920             var img = new Image();
47921             img.onload = function(){
47922                 this.reset();
47923                 this.canvasElCtx().drawImage(img, 0, 0);
47924             }.bind(this);
47925             img.src = e.target.result;
47926         }.bind(this);
47927         
47928         reader.readAsDataURL(e.target.files[0]);
47929     },
47930     
47931     // Bezier Point Constructor
47932     Point: (function () {
47933         function Point(x, y, time) {
47934             this.x = x;
47935             this.y = y;
47936             this.time = time || Date.now();
47937         }
47938         Point.prototype.distanceTo = function (start) {
47939             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
47940         };
47941         Point.prototype.equals = function (other) {
47942             return this.x === other.x && this.y === other.y && this.time === other.time;
47943         };
47944         Point.prototype.velocityFrom = function (start) {
47945             return this.time !== start.time
47946             ? this.distanceTo(start) / (this.time - start.time)
47947             : 0;
47948         };
47949         return Point;
47950     }()),
47951     
47952     
47953     // Bezier Constructor
47954     Bezier: (function () {
47955         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
47956             this.startPoint = startPoint;
47957             this.control2 = control2;
47958             this.control1 = control1;
47959             this.endPoint = endPoint;
47960             this.startWidth = startWidth;
47961             this.endWidth = endWidth;
47962         }
47963         Bezier.fromPoints = function (points, widths, scope) {
47964             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
47965             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
47966             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
47967         };
47968         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
47969             var dx1 = s1.x - s2.x;
47970             var dy1 = s1.y - s2.y;
47971             var dx2 = s2.x - s3.x;
47972             var dy2 = s2.y - s3.y;
47973             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
47974             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
47975             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
47976             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
47977             var dxm = m1.x - m2.x;
47978             var dym = m1.y - m2.y;
47979             var k = l2 / (l1 + l2);
47980             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
47981             var tx = s2.x - cm.x;
47982             var ty = s2.y - cm.y;
47983             return {
47984                 c1: new scope.Point(m1.x + tx, m1.y + ty),
47985                 c2: new scope.Point(m2.x + tx, m2.y + ty)
47986             };
47987         };
47988         Bezier.prototype.length = function () {
47989             var steps = 10;
47990             var length = 0;
47991             var px;
47992             var py;
47993             for (var i = 0; i <= steps; i += 1) {
47994                 var t = i / steps;
47995                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
47996                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
47997                 if (i > 0) {
47998                     var xdiff = cx - px;
47999                     var ydiff = cy - py;
48000                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
48001                 }
48002                 px = cx;
48003                 py = cy;
48004             }
48005             return length;
48006         };
48007         Bezier.prototype.point = function (t, start, c1, c2, end) {
48008             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
48009             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
48010             + (3.0 * c2 * (1.0 - t) * t * t)
48011             + (end * t * t * t);
48012         };
48013         return Bezier;
48014     }()),
48015     
48016     throttleStroke: function(fn, wait) {
48017       if (wait === void 0) { wait = 250; }
48018       var previous = 0;
48019       var timeout = null;
48020       var result;
48021       var storedContext;
48022       var storedArgs;
48023       var later = function () {
48024           previous = Date.now();
48025           timeout = null;
48026           result = fn.apply(storedContext, storedArgs);
48027           if (!timeout) {
48028               storedContext = null;
48029               storedArgs = [];
48030           }
48031       };
48032       return function wrapper() {
48033           var args = [];
48034           for (var _i = 0; _i < arguments.length; _i++) {
48035               args[_i] = arguments[_i];
48036           }
48037           var now = Date.now();
48038           var remaining = wait - (now - previous);
48039           storedContext = this;
48040           storedArgs = args;
48041           if (remaining <= 0 || remaining > wait) {
48042               if (timeout) {
48043                   clearTimeout(timeout);
48044                   timeout = null;
48045               }
48046               previous = now;
48047               result = fn.apply(storedContext, storedArgs);
48048               if (!timeout) {
48049                   storedContext = null;
48050                   storedArgs = [];
48051               }
48052           }
48053           else if (!timeout) {
48054               timeout = window.setTimeout(later, remaining);
48055           }
48056           return result;
48057       };
48058   }
48059   
48060 });
48061
48062  
48063
48064  // old names for form elements
48065 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
48066 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
48067 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
48068 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
48069 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
48070 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
48071 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
48072 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
48073 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
48074 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
48075 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
48076 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
48077 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
48078 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
48079 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
48080 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
48081 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
48082 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
48083 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
48084 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
48085 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
48086 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
48087 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
48088 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
48089 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
48090 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
48091
48092 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
48093 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
48094
48095 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
48096 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
48097
48098 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
48099 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
48100 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
48101 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
48102