Fix #7189 - simplify array grid example
[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  *
9169  * 
9170  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9171  * 
9172  * @constructor
9173  * Create a new Table
9174  * @param {Object} config The config object
9175  */
9176
9177 Roo.bootstrap.Table = function(config)
9178 {
9179     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9180      
9181     // BC...
9182     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9183     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9184     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9185     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9186     
9187     this.view = this; // compat with grid.
9188     
9189     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9190     if (this.sm) {
9191         this.sm.grid = this;
9192         this.selModel = Roo.factory(this.sm, Roo.grid);
9193         this.sm = this.selModel;
9194         this.sm.xmodule = this.xmodule || false;
9195     }
9196     
9197     if (this.cm && typeof(this.cm.config) == 'undefined') {
9198         this.colModel = new Roo.grid.ColumnModel(this.cm);
9199         this.cm = this.colModel;
9200         this.cm.xmodule = this.xmodule || false;
9201     }
9202     if (this.store) {
9203         this.store= Roo.factory(this.store, Roo.data);
9204         this.ds = this.store;
9205         this.ds.xmodule = this.xmodule || false;
9206          
9207     }
9208     if (this.footer && this.store) {
9209         this.footer.dataSource = this.ds;
9210         this.footer = Roo.factory(this.footer);
9211     }
9212     
9213     /** @private */
9214     this.addEvents({
9215         /**
9216          * @event cellclick
9217          * Fires when a cell is clicked
9218          * @param {Roo.bootstrap.Table} this
9219          * @param {Roo.Element} el
9220          * @param {Number} rowIndex
9221          * @param {Number} columnIndex
9222          * @param {Roo.EventObject} e
9223          */
9224         "cellclick" : true,
9225         /**
9226          * @event celldblclick
9227          * Fires when a cell is double clicked
9228          * @param {Roo.bootstrap.Table} this
9229          * @param {Roo.Element} el
9230          * @param {Number} rowIndex
9231          * @param {Number} columnIndex
9232          * @param {Roo.EventObject} e
9233          */
9234         "celldblclick" : true,
9235         /**
9236          * @event rowclick
9237          * Fires when a row is clicked
9238          * @param {Roo.bootstrap.Table} this
9239          * @param {Roo.Element} el
9240          * @param {Number} rowIndex
9241          * @param {Roo.EventObject} e
9242          */
9243         "rowclick" : true,
9244         /**
9245          * @event rowdblclick
9246          * Fires when a row is double clicked
9247          * @param {Roo.bootstrap.Table} this
9248          * @param {Roo.Element} el
9249          * @param {Number} rowIndex
9250          * @param {Roo.EventObject} e
9251          */
9252         "rowdblclick" : true,
9253         /**
9254          * @event mouseover
9255          * Fires when a mouseover occur
9256          * @param {Roo.bootstrap.Table} this
9257          * @param {Roo.Element} el
9258          * @param {Number} rowIndex
9259          * @param {Number} columnIndex
9260          * @param {Roo.EventObject} e
9261          */
9262         "mouseover" : true,
9263         /**
9264          * @event mouseout
9265          * Fires when a mouseout occur
9266          * @param {Roo.bootstrap.Table} this
9267          * @param {Roo.Element} el
9268          * @param {Number} rowIndex
9269          * @param {Number} columnIndex
9270          * @param {Roo.EventObject} e
9271          */
9272         "mouseout" : true,
9273         /**
9274          * @event rowclass
9275          * Fires when a row is rendered, so you can change add a style to it.
9276          * @param {Roo.bootstrap.Table} this
9277          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9278          */
9279         'rowclass' : true,
9280           /**
9281          * @event rowsrendered
9282          * Fires when all the  rows have been rendered
9283          * @param {Roo.bootstrap.Table} this
9284          */
9285         'rowsrendered' : true,
9286         /**
9287          * @event contextmenu
9288          * The raw contextmenu event for the entire grid.
9289          * @param {Roo.EventObject} e
9290          */
9291         "contextmenu" : true,
9292         /**
9293          * @event rowcontextmenu
9294          * Fires when a row is right clicked
9295          * @param {Roo.bootstrap.Table} this
9296          * @param {Number} rowIndex
9297          * @param {Roo.EventObject} e
9298          */
9299         "rowcontextmenu" : true,
9300         /**
9301          * @event cellcontextmenu
9302          * Fires when a cell is right clicked
9303          * @param {Roo.bootstrap.Table} this
9304          * @param {Number} rowIndex
9305          * @param {Number} cellIndex
9306          * @param {Roo.EventObject} e
9307          */
9308          "cellcontextmenu" : true,
9309          /**
9310          * @event headercontextmenu
9311          * Fires when a header is right clicked
9312          * @param {Roo.bootstrap.Table} this
9313          * @param {Number} columnIndex
9314          * @param {Roo.EventObject} e
9315          */
9316         "headercontextmenu" : true,
9317         /**
9318          * @event mousedown
9319          * The raw mousedown event for the entire grid.
9320          * @param {Roo.EventObject} e
9321          */
9322         "mousedown" : true
9323         
9324     });
9325 };
9326
9327 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9328     
9329     cls: false,
9330     
9331     empty_results : '',
9332     striped : false,
9333     scrollBody : false,
9334     bordered: false,
9335     hover:  false,
9336     condensed : false,
9337     responsive : false,
9338     sm : false,
9339     cm : false,
9340     store : false,
9341     loadMask : false,
9342     footerShow : true,
9343     headerShow : true,
9344     enableColumnResize: true,
9345   
9346     rowSelection : false,
9347     cellSelection : false,
9348     layout : false,
9349
9350     minColumnWidth : 50,
9351     
9352     // Roo.Element - the tbody
9353     bodyEl: false,  // <tbody> Roo.Element - thead element    
9354     headEl: false,  // <thead> Roo.Element - thead element
9355     resizeProxy : false, // proxy element for dragging?
9356
9357
9358     
9359     container: false, // used by gridpanel...
9360     
9361     lazyLoad : false,
9362     
9363     CSS : Roo.util.CSS,
9364     
9365     auto_hide_footer : false,
9366     
9367     view: false, // actually points to this..
9368     
9369     getAutoCreate : function()
9370     {
9371         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9372         
9373         cfg = {
9374             tag: 'table',
9375             cls : 'table', 
9376             cn : []
9377         };
9378         // this get's auto added by panel.Grid
9379         if (this.scrollBody) {
9380             cfg.cls += ' table-body-fixed';
9381         }    
9382         if (this.striped) {
9383             cfg.cls += ' table-striped';
9384         }
9385         
9386         if (this.hover) {
9387             cfg.cls += ' table-hover';
9388         }
9389         if (this.bordered) {
9390             cfg.cls += ' table-bordered';
9391         }
9392         if (this.condensed) {
9393             cfg.cls += ' table-condensed';
9394         }
9395         
9396         if (this.responsive) {
9397             cfg.cls += ' table-responsive';
9398         }
9399         
9400         if (this.cls) {
9401             cfg.cls+=  ' ' +this.cls;
9402         }
9403         
9404         
9405         
9406         if (this.layout) {
9407             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9408         }
9409         
9410         if(this.store || this.cm){
9411             if(this.headerShow){
9412                 cfg.cn.push(this.renderHeader());
9413             }
9414             
9415             cfg.cn.push(this.renderBody());
9416             
9417             if(this.footerShow){
9418                 cfg.cn.push(this.renderFooter());
9419             }
9420             // where does this come from?
9421             //cfg.cls+=  ' TableGrid';
9422         }
9423         
9424         return { cn : [ cfg ] };
9425     },
9426     
9427     initEvents : function()
9428     {   
9429         if(!this.store || !this.cm){
9430             return;
9431         }
9432         if (this.selModel) {
9433             this.selModel.initEvents();
9434         }
9435         
9436         
9437         //Roo.log('initEvents with ds!!!!');
9438         
9439         this.bodyEl = this.el.select('tbody', true).first();
9440         this.headEl = this.el.select('thead', true).first();
9441         this.mainFoot = this.el.select('tfoot', true).first();
9442         
9443         
9444         
9445         
9446         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9447             e.on('click', this.sort, this);
9448         }, this);
9449         
9450         
9451         // why is this done????? = it breaks dialogs??
9452         //this.parent().el.setStyle('position', 'relative');
9453         
9454         
9455         if (this.footer) {
9456             this.footer.parentId = this.id;
9457             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9458             
9459             if(this.lazyLoad){
9460                 this.el.select('tfoot tr td').first().addClass('hide');
9461             }
9462         } 
9463         
9464         if(this.loadMask) {
9465             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9466         }
9467         
9468         this.store.on('load', this.onLoad, this);
9469         this.store.on('beforeload', this.onBeforeLoad, this);
9470         this.store.on('update', this.onUpdate, this);
9471         this.store.on('add', this.onAdd, this);
9472         this.store.on("clear", this.clear, this);
9473         
9474         this.el.on("contextmenu", this.onContextMenu, this);
9475         
9476         
9477         this.cm.on("headerchange", this.onHeaderChange, this);
9478         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9479
9480  //?? does bodyEl get replaced on render?
9481         this.bodyEl.on("click", this.onClick, this);
9482         this.bodyEl.on("dblclick", this.onDblClick, this);        
9483         this.bodyEl.on('scroll', this.onBodyScroll, this);
9484
9485         // guessing mainbody will work - this relays usually caught by selmodel at present.
9486         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9487   
9488   
9489         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9490         
9491   
9492         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9493             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9494         }
9495         
9496         this.initCSS();
9497     },
9498     // Compatibility with grid - we implement all the view features at present.
9499     getView : function()
9500     {
9501         return this;
9502     },
9503     
9504     initCSS : function()
9505     {
9506         
9507         
9508         var cm = this.cm, styles = [];
9509         this.CSS.removeStyleSheet(this.id + '-cssrules');
9510         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9511         // we can honour xs/sm/md/xl  as widths...
9512         // we first have to decide what widht we are currently at...
9513         var sz = Roo.getGridSize();
9514         
9515         var total = 0;
9516         var last = -1;
9517         var cols = []; // visable cols.
9518         var total_abs = 0;
9519         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9520             var w = cm.getColumnWidth(i, false);
9521             if(cm.isHidden(i)){
9522                 cols.push( { rel : false, abs : 0 });
9523                 continue;
9524             }
9525             if (w !== false) {
9526                 cols.push( { rel : false, abs : w });
9527                 total_abs += w;
9528                 last = i; // not really..
9529                 continue;
9530             }
9531             var w = cm.getColumnWidth(i, sz);
9532             if (w > 0) {
9533                 last = i
9534             }
9535             total += w;
9536             cols.push( { rel : w, abs : false });
9537         }
9538         
9539         var avail = this.bodyEl.dom.clientWidth - total_abs;
9540         
9541         var unitWidth = Math.floor(avail / total);
9542         var rem = avail - (unitWidth * total);
9543         
9544         var hidden, width, pos = 0 , splithide , left;
9545         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9546             
9547             hidden = 'display:none;';
9548             left = '';
9549             width  = 'width:0px;';
9550             splithide = '';
9551             if(!cm.isHidden(i)){
9552                 hidden = '';
9553                 
9554                 
9555                 // we can honour xs/sm/md/xl ?
9556                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9557                 if (w===0) {
9558                     hidden = 'display:none;';
9559                 }
9560                 // width should return a small number...
9561                 if (i == last) {
9562                     w+=rem; // add the remaining with..
9563                 }
9564                 pos += w;
9565                 left = "left:" + (pos -4) + "px;";
9566                 width = "width:" + w+ "px;";
9567                 
9568             }
9569             if (this.responsive) {
9570                 width = '';
9571                 left = '';
9572                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9573                 splithide = 'display: none;';
9574             }
9575             
9576             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9577             if (this.headEl) {
9578                 if (i == last) {
9579                     splithide = 'display:none;';
9580                 }
9581                 
9582                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9583                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9584                             // this is the popover version..
9585                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9586                 );
9587             }
9588             
9589         }
9590         //Roo.log(styles.join(''));
9591         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9592         
9593     },
9594     
9595     
9596     
9597     onContextMenu : function(e, t)
9598     {
9599         this.processEvent("contextmenu", e);
9600     },
9601     
9602     processEvent : function(name, e)
9603     {
9604         if (name != 'touchstart' ) {
9605             this.fireEvent(name, e);    
9606         }
9607         
9608         var t = e.getTarget();
9609         
9610         var cell = Roo.get(t);
9611         
9612         if(!cell){
9613             return;
9614         }
9615         
9616         if(cell.findParent('tfoot', false, true)){
9617             return;
9618         }
9619         
9620         if(cell.findParent('thead', false, true)){
9621             
9622             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9623                 cell = Roo.get(t).findParent('th', false, true);
9624                 if (!cell) {
9625                     Roo.log("failed to find th in thead?");
9626                     Roo.log(e.getTarget());
9627                     return;
9628                 }
9629             }
9630             
9631             var cellIndex = cell.dom.cellIndex;
9632             
9633             var ename = name == 'touchstart' ? 'click' : name;
9634             this.fireEvent("header" + ename, this, cellIndex, e);
9635             
9636             return;
9637         }
9638         
9639         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9640             cell = Roo.get(t).findParent('td', false, true);
9641             if (!cell) {
9642                 Roo.log("failed to find th in tbody?");
9643                 Roo.log(e.getTarget());
9644                 return;
9645             }
9646         }
9647         
9648         var row = cell.findParent('tr', false, true);
9649         var cellIndex = cell.dom.cellIndex;
9650         var rowIndex = row.dom.rowIndex - 1;
9651         
9652         if(row !== false){
9653             
9654             this.fireEvent("row" + name, this, rowIndex, e);
9655             
9656             if(cell !== false){
9657             
9658                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9659             }
9660         }
9661         
9662     },
9663     
9664     onMouseover : function(e, el)
9665     {
9666         var cell = Roo.get(el);
9667         
9668         if(!cell){
9669             return;
9670         }
9671         
9672         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9673             cell = cell.findParent('td', false, true);
9674         }
9675         
9676         var row = cell.findParent('tr', false, true);
9677         var cellIndex = cell.dom.cellIndex;
9678         var rowIndex = row.dom.rowIndex - 1; // start from 0
9679         
9680         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9681         
9682     },
9683     
9684     onMouseout : function(e, el)
9685     {
9686         var cell = Roo.get(el);
9687         
9688         if(!cell){
9689             return;
9690         }
9691         
9692         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9693             cell = cell.findParent('td', false, true);
9694         }
9695         
9696         var row = cell.findParent('tr', false, true);
9697         var cellIndex = cell.dom.cellIndex;
9698         var rowIndex = row.dom.rowIndex - 1; // start from 0
9699         
9700         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9701         
9702     },
9703     
9704     onClick : function(e, el)
9705     {
9706         var cell = Roo.get(el);
9707         
9708         if(!cell || (!this.cellSelection && !this.rowSelection)){
9709             return;
9710         }
9711         
9712         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9713             cell = cell.findParent('td', false, true);
9714         }
9715         
9716         if(!cell || typeof(cell) == 'undefined'){
9717             return;
9718         }
9719         
9720         var row = cell.findParent('tr', false, true);
9721         
9722         if(!row || typeof(row) == 'undefined'){
9723             return;
9724         }
9725         
9726         var cellIndex = cell.dom.cellIndex;
9727         var rowIndex = this.getRowIndex(row);
9728         
9729         // why??? - should these not be based on SelectionModel?
9730         //if(this.cellSelection){
9731             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9732         //}
9733         
9734         //if(this.rowSelection){
9735             this.fireEvent('rowclick', this, row, rowIndex, e);
9736         //}
9737          
9738     },
9739         
9740     onDblClick : function(e,el)
9741     {
9742         var cell = Roo.get(el);
9743         
9744         if(!cell || (!this.cellSelection && !this.rowSelection)){
9745             return;
9746         }
9747         
9748         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9749             cell = cell.findParent('td', false, true);
9750         }
9751         
9752         if(!cell || typeof(cell) == 'undefined'){
9753             return;
9754         }
9755         
9756         var row = cell.findParent('tr', false, true);
9757         
9758         if(!row || typeof(row) == 'undefined'){
9759             return;
9760         }
9761         
9762         var cellIndex = cell.dom.cellIndex;
9763         var rowIndex = this.getRowIndex(row);
9764         
9765         if(this.cellSelection){
9766             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9767         }
9768         
9769         if(this.rowSelection){
9770             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9771         }
9772     },
9773     findRowIndex : function(el)
9774     {
9775         var cell = Roo.get(el);
9776         if(!cell) {
9777             return false;
9778         }
9779         var row = cell.findParent('tr', false, true);
9780         
9781         if(!row || typeof(row) == 'undefined'){
9782             return false;
9783         }
9784         return this.getRowIndex(row);
9785     },
9786     sort : function(e,el)
9787     {
9788         var col = Roo.get(el);
9789         
9790         if(!col.hasClass('sortable')){
9791             return;
9792         }
9793         
9794         var sort = col.attr('sort');
9795         var dir = 'ASC';
9796         
9797         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9798             dir = 'DESC';
9799         }
9800         
9801         this.store.sortInfo = {field : sort, direction : dir};
9802         
9803         if (this.footer) {
9804             Roo.log("calling footer first");
9805             this.footer.onClick('first');
9806         } else {
9807         
9808             this.store.load({ params : { start : 0 } });
9809         }
9810     },
9811     
9812     renderHeader : function()
9813     {
9814         var header = {
9815             tag: 'thead',
9816             cn : []
9817         };
9818         
9819         var cm = this.cm;
9820         this.totalWidth = 0;
9821         
9822         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9823             
9824             var config = cm.config[i];
9825             
9826             var c = {
9827                 tag: 'th',
9828                 cls : 'x-hcol-' + i,
9829                 style : '',
9830                 
9831                 html: cm.getColumnHeader(i)
9832             };
9833             
9834             var tooltip = cm.getColumnTooltip(i);
9835             if (tooltip) {
9836                 c.tooltip = tooltip;
9837             }
9838             
9839             
9840             var hh = '';
9841             
9842             if(typeof(config.sortable) != 'undefined' && config.sortable){
9843                 c.cls += ' sortable';
9844                 c.html = '<i class="fa"></i>' + c.html;
9845             }
9846             
9847             // could use BS4 hidden-..-down 
9848             
9849             if(typeof(config.lgHeader) != 'undefined'){
9850                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9851             }
9852             
9853             if(typeof(config.mdHeader) != 'undefined'){
9854                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9855             }
9856             
9857             if(typeof(config.smHeader) != 'undefined'){
9858                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9859             }
9860             
9861             if(typeof(config.xsHeader) != 'undefined'){
9862                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9863             }
9864             
9865             if(hh.length){
9866                 c.html = hh;
9867             }
9868             
9869             if(typeof(config.tooltip) != 'undefined'){
9870                 c.tooltip = config.tooltip;
9871             }
9872             
9873             if(typeof(config.colspan) != 'undefined'){
9874                 c.colspan = config.colspan;
9875             }
9876             
9877             // hidden is handled by CSS now
9878             
9879             if(typeof(config.dataIndex) != 'undefined'){
9880                 c.sort = config.dataIndex;
9881             }
9882             
9883            
9884             
9885             if(typeof(config.align) != 'undefined' && config.align.length){
9886                 c.style += ' text-align:' + config.align + ';';
9887             }
9888             
9889             /* width is done in CSS
9890              *if(typeof(config.width) != 'undefined'){
9891                 c.style += ' width:' + config.width + 'px;';
9892                 this.totalWidth += config.width;
9893             } else {
9894                 this.totalWidth += 100; // assume minimum of 100 per column?
9895             }
9896             */
9897             
9898             if(typeof(config.cls) != 'undefined'){
9899                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9900             }
9901             // this is the bit that doesnt reall work at all...
9902             
9903             if (this.responsive) {
9904                  
9905             
9906                 ['xs','sm','md','lg'].map(function(size){
9907                     
9908                     if(typeof(config[size]) == 'undefined'){
9909                         return;
9910                     }
9911                      
9912                     if (!config[size]) { // 0 = hidden
9913                         // BS 4 '0' is treated as hide that column and below.
9914                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9915                         return;
9916                     }
9917                     
9918                     c.cls += ' col-' + size + '-' + config[size] + (
9919                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9920                     );
9921                     
9922                     
9923                 });
9924             }
9925             // at the end?
9926             
9927             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9928             
9929             
9930             
9931             
9932             header.cn.push(c)
9933         }
9934         
9935         return header;
9936     },
9937     
9938     renderBody : function()
9939     {
9940         var body = {
9941             tag: 'tbody',
9942             cn : [
9943                 {
9944                     tag: 'tr',
9945                     cn : [
9946                         {
9947                             tag : 'td',
9948                             colspan :  this.cm.getColumnCount()
9949                         }
9950                     ]
9951                 }
9952             ]
9953         };
9954         
9955         return body;
9956     },
9957     
9958     renderFooter : function()
9959     {
9960         var footer = {
9961             tag: 'tfoot',
9962             cn : [
9963                 {
9964                     tag: 'tr',
9965                     cn : [
9966                         {
9967                             tag : 'td',
9968                             colspan :  this.cm.getColumnCount()
9969                         }
9970                     ]
9971                 }
9972             ]
9973         };
9974         
9975         return footer;
9976     },
9977     
9978     
9979     
9980     onLoad : function()
9981     {
9982 //        Roo.log('ds onload');
9983         this.clear();
9984         
9985         var _this = this;
9986         var cm = this.cm;
9987         var ds = this.store;
9988         
9989         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9990             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9991             if (_this.store.sortInfo) {
9992                     
9993                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9994                     e.select('i', true).addClass(['fa-arrow-up']);
9995                 }
9996                 
9997                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9998                     e.select('i', true).addClass(['fa-arrow-down']);
9999                 }
10000             }
10001         });
10002         
10003         var tbody =  this.bodyEl;
10004               
10005         if(ds.getCount() > 0){
10006             ds.data.each(function(d,rowIndex){
10007                 var row =  this.renderRow(cm, ds, rowIndex);
10008                 
10009                 tbody.createChild(row);
10010                 
10011                 var _this = this;
10012                 
10013                 if(row.cellObjects.length){
10014                     Roo.each(row.cellObjects, function(r){
10015                         _this.renderCellObject(r);
10016                     })
10017                 }
10018                 
10019             }, this);
10020         } else if (this.empty_results.length) {
10021             this.el.mask(this.empty_results, 'no-spinner');
10022         }
10023         
10024         var tfoot = this.el.select('tfoot', true).first();
10025         
10026         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10027             
10028             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10029             
10030             var total = this.ds.getTotalCount();
10031             
10032             if(this.footer.pageSize < total){
10033                 this.mainFoot.show();
10034             }
10035         }
10036         
10037         Roo.each(this.el.select('tbody td', true).elements, function(e){
10038             e.on('mouseover', _this.onMouseover, _this);
10039         });
10040         
10041         Roo.each(this.el.select('tbody td', true).elements, function(e){
10042             e.on('mouseout', _this.onMouseout, _this);
10043         });
10044         this.fireEvent('rowsrendered', this);
10045         
10046         this.autoSize();
10047         
10048         this.initCSS(); /// resize cols
10049
10050         
10051     },
10052     
10053     
10054     onUpdate : function(ds,record)
10055     {
10056         this.refreshRow(record);
10057         this.autoSize();
10058     },
10059     
10060     onRemove : function(ds, record, index, isUpdate){
10061         if(isUpdate !== true){
10062             this.fireEvent("beforerowremoved", this, index, record);
10063         }
10064         var bt = this.bodyEl.dom;
10065         
10066         var rows = this.el.select('tbody > tr', true).elements;
10067         
10068         if(typeof(rows[index]) != 'undefined'){
10069             bt.removeChild(rows[index].dom);
10070         }
10071         
10072 //        if(bt.rows[index]){
10073 //            bt.removeChild(bt.rows[index]);
10074 //        }
10075         
10076         if(isUpdate !== true){
10077             //this.stripeRows(index);
10078             //this.syncRowHeights(index, index);
10079             //this.layout();
10080             this.fireEvent("rowremoved", this, index, record);
10081         }
10082     },
10083     
10084     onAdd : function(ds, records, rowIndex)
10085     {
10086         //Roo.log('on Add called');
10087         // - note this does not handle multiple adding very well..
10088         var bt = this.bodyEl.dom;
10089         for (var i =0 ; i < records.length;i++) {
10090             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10091             //Roo.log(records[i]);
10092             //Roo.log(this.store.getAt(rowIndex+i));
10093             this.insertRow(this.store, rowIndex + i, false);
10094             return;
10095         }
10096         
10097     },
10098     
10099     
10100     refreshRow : function(record){
10101         var ds = this.store, index;
10102         if(typeof record == 'number'){
10103             index = record;
10104             record = ds.getAt(index);
10105         }else{
10106             index = ds.indexOf(record);
10107             if (index < 0) {
10108                 return; // should not happen - but seems to 
10109             }
10110         }
10111         this.insertRow(ds, index, true);
10112         this.autoSize();
10113         this.onRemove(ds, record, index+1, true);
10114         this.autoSize();
10115         //this.syncRowHeights(index, index);
10116         //this.layout();
10117         this.fireEvent("rowupdated", this, index, record);
10118     },
10119     // private - called by RowSelection
10120     onRowSelect : function(rowIndex){
10121         var row = this.getRowDom(rowIndex);
10122         row.addClass(['bg-info','info']);
10123     },
10124     // private - called by RowSelection
10125     onRowDeselect : function(rowIndex)
10126     {
10127         if (rowIndex < 0) {
10128             return;
10129         }
10130         var row = this.getRowDom(rowIndex);
10131         row.removeClass(['bg-info','info']);
10132     },
10133       /**
10134      * Focuses the specified row.
10135      * @param {Number} row The row index
10136      */
10137     focusRow : function(row)
10138     {
10139         //Roo.log('GridView.focusRow');
10140         var x = this.bodyEl.dom.scrollLeft;
10141         this.focusCell(row, 0, false);
10142         this.bodyEl.dom.scrollLeft = x;
10143
10144     },
10145      /**
10146      * Focuses the specified cell.
10147      * @param {Number} row The row index
10148      * @param {Number} col The column index
10149      * @param {Boolean} hscroll false to disable horizontal scrolling
10150      */
10151     focusCell : function(row, col, hscroll)
10152     {
10153         //Roo.log('GridView.focusCell');
10154         var el = this.ensureVisible(row, col, hscroll);
10155         // not sure what focusEL achives = it's a <a> pos relative 
10156         //this.focusEl.alignTo(el, "tl-tl");
10157         //if(Roo.isGecko){
10158         //    this.focusEl.focus();
10159         //}else{
10160         //    this.focusEl.focus.defer(1, this.focusEl);
10161         //}
10162     },
10163     
10164      /**
10165      * Scrolls the specified cell into view
10166      * @param {Number} row The row index
10167      * @param {Number} col The column index
10168      * @param {Boolean} hscroll false to disable horizontal scrolling
10169      */
10170     ensureVisible : function(row, col, hscroll)
10171     {
10172         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10173         //return null; //disable for testing.
10174         if(typeof row != "number"){
10175             row = row.rowIndex;
10176         }
10177         if(row < 0 && row >= this.ds.getCount()){
10178             return  null;
10179         }
10180         col = (col !== undefined ? col : 0);
10181         var cm = this.cm;
10182         while(cm.isHidden(col)){
10183             col++;
10184         }
10185
10186         var el = this.getCellDom(row, col);
10187         if(!el){
10188             return null;
10189         }
10190         var c = this.bodyEl.dom;
10191
10192         var ctop = parseInt(el.offsetTop, 10);
10193         var cleft = parseInt(el.offsetLeft, 10);
10194         var cbot = ctop + el.offsetHeight;
10195         var cright = cleft + el.offsetWidth;
10196
10197         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10198         var ch = 0; //?? header is not withing the area?
10199         var stop = parseInt(c.scrollTop, 10);
10200         var sleft = parseInt(c.scrollLeft, 10);
10201         var sbot = stop + ch;
10202         var sright = sleft + c.clientWidth;
10203         /*
10204         Roo.log('GridView.ensureVisible:' +
10205                 ' ctop:' + ctop +
10206                 ' c.clientHeight:' + c.clientHeight +
10207                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10208                 ' stop:' + stop +
10209                 ' cbot:' + cbot +
10210                 ' sbot:' + sbot +
10211                 ' ch:' + ch  
10212                 );
10213         */
10214         if(ctop < stop){
10215             c.scrollTop = ctop;
10216             //Roo.log("set scrolltop to ctop DISABLE?");
10217         }else if(cbot > sbot){
10218             //Roo.log("set scrolltop to cbot-ch");
10219             c.scrollTop = cbot-ch;
10220         }
10221
10222         if(hscroll !== false){
10223             if(cleft < sleft){
10224                 c.scrollLeft = cleft;
10225             }else if(cright > sright){
10226                 c.scrollLeft = cright-c.clientWidth;
10227             }
10228         }
10229
10230         return el;
10231     },
10232     
10233     
10234     insertRow : function(dm, rowIndex, isUpdate){
10235         
10236         if(!isUpdate){
10237             this.fireEvent("beforerowsinserted", this, rowIndex);
10238         }
10239             //var s = this.getScrollState();
10240         var row = this.renderRow(this.cm, this.store, rowIndex);
10241         // insert before rowIndex..
10242         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10243         
10244         var _this = this;
10245                 
10246         if(row.cellObjects.length){
10247             Roo.each(row.cellObjects, function(r){
10248                 _this.renderCellObject(r);
10249             })
10250         }
10251             
10252         if(!isUpdate){
10253             this.fireEvent("rowsinserted", this, rowIndex);
10254             //this.syncRowHeights(firstRow, lastRow);
10255             //this.stripeRows(firstRow);
10256             //this.layout();
10257         }
10258         
10259     },
10260     
10261     
10262     getRowDom : function(rowIndex)
10263     {
10264         var rows = this.el.select('tbody > tr', true).elements;
10265         
10266         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10267         
10268     },
10269     getCellDom : function(rowIndex, colIndex)
10270     {
10271         var row = this.getRowDom(rowIndex);
10272         if (row === false) {
10273             return false;
10274         }
10275         var cols = row.select('td', true).elements;
10276         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10277         
10278     },
10279     
10280     // returns the object tree for a tr..
10281   
10282     
10283     renderRow : function(cm, ds, rowIndex) 
10284     {
10285         var d = ds.getAt(rowIndex);
10286         
10287         var row = {
10288             tag : 'tr',
10289             cls : 'x-row-' + rowIndex,
10290             cn : []
10291         };
10292             
10293         var cellObjects = [];
10294         
10295         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10296             var config = cm.config[i];
10297             
10298             var renderer = cm.getRenderer(i);
10299             var value = '';
10300             var id = false;
10301             
10302             if(typeof(renderer) !== 'undefined'){
10303                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10304             }
10305             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10306             // and are rendered into the cells after the row is rendered - using the id for the element.
10307             
10308             if(typeof(value) === 'object'){
10309                 id = Roo.id();
10310                 cellObjects.push({
10311                     container : id,
10312                     cfg : value 
10313                 })
10314             }
10315             
10316             var rowcfg = {
10317                 record: d,
10318                 rowIndex : rowIndex,
10319                 colIndex : i,
10320                 rowClass : ''
10321             };
10322
10323             this.fireEvent('rowclass', this, rowcfg);
10324             
10325             var td = {
10326                 tag: 'td',
10327                 // this might end up displaying HTML?
10328                 // this is too messy... - better to only do it on columsn you know are going to be too long
10329                 //tooltip : (typeof(value) === 'object') ? '' : value,
10330                 cls : rowcfg.rowClass + ' x-col-' + i,
10331                 style: '',
10332                 html: (typeof(value) === 'object') ? '' : value
10333             };
10334             
10335             if (id) {
10336                 td.id = id;
10337             }
10338             
10339             if(typeof(config.colspan) != 'undefined'){
10340                 td.colspan = config.colspan;
10341             }
10342             
10343             
10344             
10345             if(typeof(config.align) != 'undefined' && config.align.length){
10346                 td.style += ' text-align:' + config.align + ';';
10347             }
10348             if(typeof(config.valign) != 'undefined' && config.valign.length){
10349                 td.style += ' vertical-align:' + config.valign + ';';
10350             }
10351             /*
10352             if(typeof(config.width) != 'undefined'){
10353                 td.style += ' width:' +  config.width + 'px;';
10354             }
10355             */
10356             
10357             if(typeof(config.cursor) != 'undefined'){
10358                 td.style += ' cursor:' +  config.cursor + ';';
10359             }
10360             
10361             if(typeof(config.cls) != 'undefined'){
10362                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10363             }
10364             if (this.responsive) {
10365                 ['xs','sm','md','lg'].map(function(size){
10366                     
10367                     if(typeof(config[size]) == 'undefined'){
10368                         return;
10369                     }
10370                     
10371                     
10372                       
10373                     if (!config[size]) { // 0 = hidden
10374                         // BS 4 '0' is treated as hide that column and below.
10375                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10376                         return;
10377                     }
10378                     
10379                     td.cls += ' col-' + size + '-' + config[size] + (
10380                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10381                     );
10382                      
10383     
10384                 });
10385             }
10386             row.cn.push(td);
10387            
10388         }
10389         
10390         row.cellObjects = cellObjects;
10391         
10392         return row;
10393           
10394     },
10395     
10396     
10397     
10398     onBeforeLoad : function()
10399     {
10400         this.el.unmask(); // if needed.
10401     },
10402      /**
10403      * Remove all rows
10404      */
10405     clear : function()
10406     {
10407         this.el.select('tbody', true).first().dom.innerHTML = '';
10408     },
10409     /**
10410      * Show or hide a row.
10411      * @param {Number} rowIndex to show or hide
10412      * @param {Boolean} state hide
10413      */
10414     setRowVisibility : function(rowIndex, state)
10415     {
10416         var bt = this.bodyEl.dom;
10417         
10418         var rows = this.el.select('tbody > tr', true).elements;
10419         
10420         if(typeof(rows[rowIndex]) == 'undefined'){
10421             return;
10422         }
10423         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10424         
10425     },
10426     
10427     
10428     getSelectionModel : function(){
10429         if(!this.selModel){
10430             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10431         }
10432         return this.selModel;
10433     },
10434     /*
10435      * Render the Roo.bootstrap object from renderder
10436      */
10437     renderCellObject : function(r)
10438     {
10439         var _this = this;
10440         
10441         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10442         
10443         var t = r.cfg.render(r.container);
10444         
10445         if(r.cfg.cn){
10446             Roo.each(r.cfg.cn, function(c){
10447                 var child = {
10448                     container: t.getChildContainer(),
10449                     cfg: c
10450                 };
10451                 _this.renderCellObject(child);
10452             })
10453         }
10454     },
10455     /**
10456      * get the Row Index from a dom element.
10457      * @param {Roo.Element} row The row to look for
10458      * @returns {Number} the row
10459      */
10460     getRowIndex : function(row)
10461     {
10462         var rowIndex = -1;
10463         
10464         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10465             if(el != row){
10466                 return;
10467             }
10468             
10469             rowIndex = index;
10470         });
10471         
10472         return rowIndex;
10473     },
10474     /**
10475      * get the header TH element for columnIndex
10476      * @param {Number} columnIndex
10477      * @returns {Roo.Element}
10478      */
10479     getHeaderIndex: function(colIndex)
10480     {
10481         var cols = this.headEl.select('th', true).elements;
10482         return cols[colIndex]; 
10483     },
10484     /**
10485      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10486      * @param {domElement} cell to look for
10487      * @returns {Number} the column
10488      */
10489     getCellIndex : function(cell)
10490     {
10491         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10492         if(id){
10493             return parseInt(id[1], 10);
10494         }
10495         return 0;
10496     },
10497      /**
10498      * Returns the grid's underlying element = used by panel.Grid
10499      * @return {Element} The element
10500      */
10501     getGridEl : function(){
10502         return this.el;
10503     },
10504      /**
10505      * Forces a resize - used by panel.Grid
10506      * @return {Element} The element
10507      */
10508     autoSize : function()
10509     {
10510         //var ctr = Roo.get(this.container.dom.parentElement);
10511         var ctr = Roo.get(this.el.dom);
10512         
10513         var thd = this.getGridEl().select('thead',true).first();
10514         var tbd = this.getGridEl().select('tbody', true).first();
10515         var tfd = this.getGridEl().select('tfoot', true).first();
10516         
10517         var cw = ctr.getWidth();
10518         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10519         
10520         if (tbd) {
10521             
10522             tbd.setWidth(ctr.getWidth());
10523             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10524             // this needs fixing for various usage - currently only hydra job advers I think..
10525             //tdb.setHeight(
10526             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10527             //); 
10528             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10529             cw -= barsize;
10530         }
10531         cw = Math.max(cw, this.totalWidth);
10532         this.getGridEl().select('tbody tr',true).setWidth(cw);
10533         this.initCSS();
10534         
10535         // resize 'expandable coloumn?
10536         
10537         return; // we doe not have a view in this design..
10538         
10539     },
10540     onBodyScroll: function()
10541     {
10542         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10543         if(this.headEl){
10544             this.headEl.setStyle({
10545                 'position' : 'relative',
10546                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10547             });
10548         }
10549         
10550         if(this.lazyLoad){
10551             
10552             var scrollHeight = this.bodyEl.dom.scrollHeight;
10553             
10554             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10555             
10556             var height = this.bodyEl.getHeight();
10557             
10558             if(scrollHeight - height == scrollTop) {
10559                 
10560                 var total = this.ds.getTotalCount();
10561                 
10562                 if(this.footer.cursor + this.footer.pageSize < total){
10563                     
10564                     this.footer.ds.load({
10565                         params : {
10566                             start : this.footer.cursor + this.footer.pageSize,
10567                             limit : this.footer.pageSize
10568                         },
10569                         add : true
10570                     });
10571                 }
10572             }
10573             
10574         }
10575     },
10576     onColumnSplitterMoved : function(i, diff)
10577     {
10578         this.userResized = true;
10579         
10580         var cm = this.colModel;
10581         
10582         var w = this.getHeaderIndex(i).getWidth() + diff;
10583         
10584         
10585         cm.setColumnWidth(i, w, true);
10586         this.initCSS();
10587         //var cid = cm.getColumnId(i); << not used in this version?
10588        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10589         
10590         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10591         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10592         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10593 */
10594         //this.updateSplitters();
10595         //this.layout(); << ??
10596         this.fireEvent("columnresize", i, w);
10597     },
10598     onHeaderChange : function()
10599     {
10600         var header = this.renderHeader();
10601         var table = this.el.select('table', true).first();
10602         
10603         this.headEl.remove();
10604         this.headEl = table.createChild(header, this.bodyEl, false);
10605         
10606         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10607             e.on('click', this.sort, this);
10608         }, this);
10609         
10610         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10611             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10612         }
10613         
10614     },
10615     
10616     onHiddenChange : function(colModel, colIndex, hidden)
10617     {
10618         /*
10619         this.cm.setHidden()
10620         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10621         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10622         
10623         this.CSS.updateRule(thSelector, "display", "");
10624         this.CSS.updateRule(tdSelector, "display", "");
10625         
10626         if(hidden){
10627             this.CSS.updateRule(thSelector, "display", "none");
10628             this.CSS.updateRule(tdSelector, "display", "none");
10629         }
10630         */
10631         // onload calls initCSS()
10632         this.onHeaderChange();
10633         this.onLoad();
10634     },
10635     
10636     setColumnWidth: function(col_index, width)
10637     {
10638         // width = "md-2 xs-2..."
10639         if(!this.colModel.config[col_index]) {
10640             return;
10641         }
10642         
10643         var w = width.split(" ");
10644         
10645         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10646         
10647         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10648         
10649         
10650         for(var j = 0; j < w.length; j++) {
10651             
10652             if(!w[j]) {
10653                 continue;
10654             }
10655             
10656             var size_cls = w[j].split("-");
10657             
10658             if(!Number.isInteger(size_cls[1] * 1)) {
10659                 continue;
10660             }
10661             
10662             if(!this.colModel.config[col_index][size_cls[0]]) {
10663                 continue;
10664             }
10665             
10666             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10667                 continue;
10668             }
10669             
10670             h_row[0].classList.replace(
10671                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10672                 "col-"+size_cls[0]+"-"+size_cls[1]
10673             );
10674             
10675             for(var i = 0; i < rows.length; i++) {
10676                 
10677                 var size_cls = w[j].split("-");
10678                 
10679                 if(!Number.isInteger(size_cls[1] * 1)) {
10680                     continue;
10681                 }
10682                 
10683                 if(!this.colModel.config[col_index][size_cls[0]]) {
10684                     continue;
10685                 }
10686                 
10687                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10688                     continue;
10689                 }
10690                 
10691                 rows[i].classList.replace(
10692                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10693                     "col-"+size_cls[0]+"-"+size_cls[1]
10694                 );
10695             }
10696             
10697             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10698         }
10699     }
10700 });
10701
10702 // currently only used to find the split on drag.. 
10703 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10704
10705 /**
10706  * @depricated
10707 */
10708 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10709 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10710 /*
10711  * - LGPL
10712  *
10713  * table cell
10714  * 
10715  */
10716
10717 /**
10718  * @class Roo.bootstrap.TableCell
10719  * @extends Roo.bootstrap.Component
10720  * @children Roo.bootstrap.Component
10721  * @parent Roo.bootstrap.TableRow
10722  * Bootstrap TableCell class
10723  * 
10724  * @cfg {String} html cell contain text
10725  * @cfg {String} cls cell class
10726  * @cfg {String} tag cell tag (td|th) default td
10727  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10728  * @cfg {String} align Aligns the content in a cell
10729  * @cfg {String} axis Categorizes cells
10730  * @cfg {String} bgcolor Specifies the background color of a cell
10731  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10732  * @cfg {Number} colspan Specifies the number of columns a cell should span
10733  * @cfg {String} headers Specifies one or more header cells a cell is related to
10734  * @cfg {Number} height Sets the height of a cell
10735  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10736  * @cfg {Number} rowspan Sets the number of rows a cell should span
10737  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10738  * @cfg {String} valign Vertical aligns the content in a cell
10739  * @cfg {Number} width Specifies the width of a cell
10740  * 
10741  * @constructor
10742  * Create a new TableCell
10743  * @param {Object} config The config object
10744  */
10745
10746 Roo.bootstrap.TableCell = function(config){
10747     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10748 };
10749
10750 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10751     
10752     html: false,
10753     cls: false,
10754     tag: false,
10755     abbr: false,
10756     align: false,
10757     axis: false,
10758     bgcolor: false,
10759     charoff: false,
10760     colspan: false,
10761     headers: false,
10762     height: false,
10763     nowrap: false,
10764     rowspan: false,
10765     scope: false,
10766     valign: false,
10767     width: false,
10768     
10769     
10770     getAutoCreate : function(){
10771         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10772         
10773         cfg = {
10774             tag: 'td'
10775         };
10776         
10777         if(this.tag){
10778             cfg.tag = this.tag;
10779         }
10780         
10781         if (this.html) {
10782             cfg.html=this.html
10783         }
10784         if (this.cls) {
10785             cfg.cls=this.cls
10786         }
10787         if (this.abbr) {
10788             cfg.abbr=this.abbr
10789         }
10790         if (this.align) {
10791             cfg.align=this.align
10792         }
10793         if (this.axis) {
10794             cfg.axis=this.axis
10795         }
10796         if (this.bgcolor) {
10797             cfg.bgcolor=this.bgcolor
10798         }
10799         if (this.charoff) {
10800             cfg.charoff=this.charoff
10801         }
10802         if (this.colspan) {
10803             cfg.colspan=this.colspan
10804         }
10805         if (this.headers) {
10806             cfg.headers=this.headers
10807         }
10808         if (this.height) {
10809             cfg.height=this.height
10810         }
10811         if (this.nowrap) {
10812             cfg.nowrap=this.nowrap
10813         }
10814         if (this.rowspan) {
10815             cfg.rowspan=this.rowspan
10816         }
10817         if (this.scope) {
10818             cfg.scope=this.scope
10819         }
10820         if (this.valign) {
10821             cfg.valign=this.valign
10822         }
10823         if (this.width) {
10824             cfg.width=this.width
10825         }
10826         
10827         
10828         return cfg;
10829     }
10830    
10831 });
10832
10833  
10834
10835  /*
10836  * - LGPL
10837  *
10838  * table row
10839  * 
10840  */
10841
10842 /**
10843  * @class Roo.bootstrap.TableRow
10844  * @extends Roo.bootstrap.Component
10845  * @children Roo.bootstrap.TableCell
10846  * @parent Roo.bootstrap.TableBody
10847  * Bootstrap TableRow class
10848  * @cfg {String} cls row class
10849  * @cfg {String} align Aligns the content in a table row
10850  * @cfg {String} bgcolor Specifies a background color for a table row
10851  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10852  * @cfg {String} valign Vertical aligns the content in a table row
10853  * 
10854  * @constructor
10855  * Create a new TableRow
10856  * @param {Object} config The config object
10857  */
10858
10859 Roo.bootstrap.TableRow = function(config){
10860     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10861 };
10862
10863 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10864     
10865     cls: false,
10866     align: false,
10867     bgcolor: false,
10868     charoff: false,
10869     valign: false,
10870     
10871     getAutoCreate : function(){
10872         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10873         
10874         cfg = {
10875             tag: 'tr'
10876         };
10877             
10878         if(this.cls){
10879             cfg.cls = this.cls;
10880         }
10881         if(this.align){
10882             cfg.align = this.align;
10883         }
10884         if(this.bgcolor){
10885             cfg.bgcolor = this.bgcolor;
10886         }
10887         if(this.charoff){
10888             cfg.charoff = this.charoff;
10889         }
10890         if(this.valign){
10891             cfg.valign = this.valign;
10892         }
10893         
10894         return cfg;
10895     }
10896    
10897 });
10898
10899  
10900
10901  /*
10902  * - LGPL
10903  *
10904  * table body
10905  * 
10906  */
10907
10908 /**
10909  * @class Roo.bootstrap.TableBody
10910  * @extends Roo.bootstrap.Component
10911  * @children Roo.bootstrap.TableRow
10912  * @parent Roo.bootstrap.Table
10913  * Bootstrap TableBody class
10914  * @cfg {String} cls element class
10915  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10916  * @cfg {String} align Aligns the content inside the element
10917  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10918  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10919  * 
10920  * @constructor
10921  * Create a new TableBody
10922  * @param {Object} config The config object
10923  */
10924
10925 Roo.bootstrap.TableBody = function(config){
10926     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10927 };
10928
10929 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10930     
10931     cls: false,
10932     tag: false,
10933     align: false,
10934     charoff: false,
10935     valign: false,
10936     
10937     getAutoCreate : function(){
10938         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10939         
10940         cfg = {
10941             tag: 'tbody'
10942         };
10943             
10944         if (this.cls) {
10945             cfg.cls=this.cls
10946         }
10947         if(this.tag){
10948             cfg.tag = this.tag;
10949         }
10950         
10951         if(this.align){
10952             cfg.align = this.align;
10953         }
10954         if(this.charoff){
10955             cfg.charoff = this.charoff;
10956         }
10957         if(this.valign){
10958             cfg.valign = this.valign;
10959         }
10960         
10961         return cfg;
10962     }
10963     
10964     
10965 //    initEvents : function()
10966 //    {
10967 //        
10968 //        if(!this.store){
10969 //            return;
10970 //        }
10971 //        
10972 //        this.store = Roo.factory(this.store, Roo.data);
10973 //        this.store.on('load', this.onLoad, this);
10974 //        
10975 //        this.store.load();
10976 //        
10977 //    },
10978 //    
10979 //    onLoad: function () 
10980 //    {   
10981 //        this.fireEvent('load', this);
10982 //    }
10983 //    
10984 //   
10985 });
10986
10987  
10988
10989  /*
10990  * Based on:
10991  * Ext JS Library 1.1.1
10992  * Copyright(c) 2006-2007, Ext JS, LLC.
10993  *
10994  * Originally Released Under LGPL - original licence link has changed is not relivant.
10995  *
10996  * Fork - LGPL
10997  * <script type="text/javascript">
10998  */
10999
11000 // as we use this in bootstrap.
11001 Roo.namespace('Roo.form');
11002  /**
11003  * @class Roo.form.Action
11004  * Internal Class used to handle form actions
11005  * @constructor
11006  * @param {Roo.form.BasicForm} el The form element or its id
11007  * @param {Object} config Configuration options
11008  */
11009
11010  
11011  
11012 // define the action interface
11013 Roo.form.Action = function(form, options){
11014     this.form = form;
11015     this.options = options || {};
11016 };
11017 /**
11018  * Client Validation Failed
11019  * @const 
11020  */
11021 Roo.form.Action.CLIENT_INVALID = 'client';
11022 /**
11023  * Server Validation Failed
11024  * @const 
11025  */
11026 Roo.form.Action.SERVER_INVALID = 'server';
11027  /**
11028  * Connect to Server Failed
11029  * @const 
11030  */
11031 Roo.form.Action.CONNECT_FAILURE = 'connect';
11032 /**
11033  * Reading Data from Server Failed
11034  * @const 
11035  */
11036 Roo.form.Action.LOAD_FAILURE = 'load';
11037
11038 Roo.form.Action.prototype = {
11039     type : 'default',
11040     failureType : undefined,
11041     response : undefined,
11042     result : undefined,
11043
11044     // interface method
11045     run : function(options){
11046
11047     },
11048
11049     // interface method
11050     success : function(response){
11051
11052     },
11053
11054     // interface method
11055     handleResponse : function(response){
11056
11057     },
11058
11059     // default connection failure
11060     failure : function(response){
11061         
11062         this.response = response;
11063         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11064         this.form.afterAction(this, false);
11065     },
11066
11067     processResponse : function(response){
11068         this.response = response;
11069         if(!response.responseText){
11070             return true;
11071         }
11072         this.result = this.handleResponse(response);
11073         return this.result;
11074     },
11075
11076     // utility functions used internally
11077     getUrl : function(appendParams){
11078         var url = this.options.url || this.form.url || this.form.el.dom.action;
11079         if(appendParams){
11080             var p = this.getParams();
11081             if(p){
11082                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11083             }
11084         }
11085         return url;
11086     },
11087
11088     getMethod : function(){
11089         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11090     },
11091
11092     getParams : function(){
11093         var bp = this.form.baseParams;
11094         var p = this.options.params;
11095         if(p){
11096             if(typeof p == "object"){
11097                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11098             }else if(typeof p == 'string' && bp){
11099                 p += '&' + Roo.urlEncode(bp);
11100             }
11101         }else if(bp){
11102             p = Roo.urlEncode(bp);
11103         }
11104         return p;
11105     },
11106
11107     createCallback : function(){
11108         return {
11109             success: this.success,
11110             failure: this.failure,
11111             scope: this,
11112             timeout: (this.form.timeout*1000),
11113             upload: this.form.fileUpload ? this.success : undefined
11114         };
11115     }
11116 };
11117
11118 Roo.form.Action.Submit = function(form, options){
11119     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11120 };
11121
11122 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11123     type : 'submit',
11124
11125     haveProgress : false,
11126     uploadComplete : false,
11127     
11128     // uploadProgress indicator.
11129     uploadProgress : function()
11130     {
11131         if (!this.form.progressUrl) {
11132             return;
11133         }
11134         
11135         if (!this.haveProgress) {
11136             Roo.MessageBox.progress("Uploading", "Uploading");
11137         }
11138         if (this.uploadComplete) {
11139            Roo.MessageBox.hide();
11140            return;
11141         }
11142         
11143         this.haveProgress = true;
11144    
11145         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11146         
11147         var c = new Roo.data.Connection();
11148         c.request({
11149             url : this.form.progressUrl,
11150             params: {
11151                 id : uid
11152             },
11153             method: 'GET',
11154             success : function(req){
11155                //console.log(data);
11156                 var rdata = false;
11157                 var edata;
11158                 try  {
11159                    rdata = Roo.decode(req.responseText)
11160                 } catch (e) {
11161                     Roo.log("Invalid data from server..");
11162                     Roo.log(edata);
11163                     return;
11164                 }
11165                 if (!rdata || !rdata.success) {
11166                     Roo.log(rdata);
11167                     Roo.MessageBox.alert(Roo.encode(rdata));
11168                     return;
11169                 }
11170                 var data = rdata.data;
11171                 
11172                 if (this.uploadComplete) {
11173                    Roo.MessageBox.hide();
11174                    return;
11175                 }
11176                    
11177                 if (data){
11178                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11179                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11180                     );
11181                 }
11182                 this.uploadProgress.defer(2000,this);
11183             },
11184        
11185             failure: function(data) {
11186                 Roo.log('progress url failed ');
11187                 Roo.log(data);
11188             },
11189             scope : this
11190         });
11191            
11192     },
11193     
11194     
11195     run : function()
11196     {
11197         // run get Values on the form, so it syncs any secondary forms.
11198         this.form.getValues();
11199         
11200         var o = this.options;
11201         var method = this.getMethod();
11202         var isPost = method == 'POST';
11203         if(o.clientValidation === false || this.form.isValid()){
11204             
11205             if (this.form.progressUrl) {
11206                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11207                     (new Date() * 1) + '' + Math.random());
11208                     
11209             } 
11210             
11211             
11212             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11213                 form:this.form.el.dom,
11214                 url:this.getUrl(!isPost),
11215                 method: method,
11216                 params:isPost ? this.getParams() : null,
11217                 isUpload: this.form.fileUpload,
11218                 formData : this.form.formData
11219             }));
11220             
11221             this.uploadProgress();
11222
11223         }else if (o.clientValidation !== false){ // client validation failed
11224             this.failureType = Roo.form.Action.CLIENT_INVALID;
11225             this.form.afterAction(this, false);
11226         }
11227     },
11228
11229     success : function(response)
11230     {
11231         this.uploadComplete= true;
11232         if (this.haveProgress) {
11233             Roo.MessageBox.hide();
11234         }
11235         
11236         
11237         var result = this.processResponse(response);
11238         if(result === true || result.success){
11239             this.form.afterAction(this, true);
11240             return;
11241         }
11242         if(result.errors){
11243             this.form.markInvalid(result.errors);
11244             this.failureType = Roo.form.Action.SERVER_INVALID;
11245         }
11246         this.form.afterAction(this, false);
11247     },
11248     failure : function(response)
11249     {
11250         this.uploadComplete= true;
11251         if (this.haveProgress) {
11252             Roo.MessageBox.hide();
11253         }
11254         
11255         this.response = response;
11256         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11257         this.form.afterAction(this, false);
11258     },
11259     
11260     handleResponse : function(response){
11261         if(this.form.errorReader){
11262             var rs = this.form.errorReader.read(response);
11263             var errors = [];
11264             if(rs.records){
11265                 for(var i = 0, len = rs.records.length; i < len; i++) {
11266                     var r = rs.records[i];
11267                     errors[i] = r.data;
11268                 }
11269             }
11270             if(errors.length < 1){
11271                 errors = null;
11272             }
11273             return {
11274                 success : rs.success,
11275                 errors : errors
11276             };
11277         }
11278         var ret = false;
11279         try {
11280             ret = Roo.decode(response.responseText);
11281         } catch (e) {
11282             ret = {
11283                 success: false,
11284                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11285                 errors : []
11286             };
11287         }
11288         return ret;
11289         
11290     }
11291 });
11292
11293
11294 Roo.form.Action.Load = function(form, options){
11295     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11296     this.reader = this.form.reader;
11297 };
11298
11299 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11300     type : 'load',
11301
11302     run : function(){
11303         
11304         Roo.Ajax.request(Roo.apply(
11305                 this.createCallback(), {
11306                     method:this.getMethod(),
11307                     url:this.getUrl(false),
11308                     params:this.getParams()
11309         }));
11310     },
11311
11312     success : function(response){
11313         
11314         var result = this.processResponse(response);
11315         if(result === true || !result.success || !result.data){
11316             this.failureType = Roo.form.Action.LOAD_FAILURE;
11317             this.form.afterAction(this, false);
11318             return;
11319         }
11320         this.form.clearInvalid();
11321         this.form.setValues(result.data);
11322         this.form.afterAction(this, true);
11323     },
11324
11325     handleResponse : function(response){
11326         if(this.form.reader){
11327             var rs = this.form.reader.read(response);
11328             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11329             return {
11330                 success : rs.success,
11331                 data : data
11332             };
11333         }
11334         return Roo.decode(response.responseText);
11335     }
11336 });
11337
11338 Roo.form.Action.ACTION_TYPES = {
11339     'load' : Roo.form.Action.Load,
11340     'submit' : Roo.form.Action.Submit
11341 };/*
11342  * - LGPL
11343  *
11344  * form
11345  *
11346  */
11347
11348 /**
11349  * @class Roo.bootstrap.form.Form
11350  * @extends Roo.bootstrap.Component
11351  * @children Roo.bootstrap.Component
11352  * Bootstrap Form class
11353  * @cfg {String} method  GET | POST (default POST)
11354  * @cfg {String} labelAlign top | left (default top)
11355  * @cfg {String} align left  | right - for navbars
11356  * @cfg {Boolean} loadMask load mask when submit (default true)
11357
11358  *
11359  * @constructor
11360  * Create a new Form
11361  * @param {Object} config The config object
11362  */
11363
11364
11365 Roo.bootstrap.form.Form = function(config){
11366     
11367     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11368     
11369     Roo.bootstrap.form.Form.popover.apply();
11370     
11371     this.addEvents({
11372         /**
11373          * @event clientvalidation
11374          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11375          * @param {Form} this
11376          * @param {Boolean} valid true if the form has passed client-side validation
11377          */
11378         clientvalidation: true,
11379         /**
11380          * @event beforeaction
11381          * Fires before any action is performed. Return false to cancel the action.
11382          * @param {Form} this
11383          * @param {Action} action The action to be performed
11384          */
11385         beforeaction: true,
11386         /**
11387          * @event actionfailed
11388          * Fires when an action fails.
11389          * @param {Form} this
11390          * @param {Action} action The action that failed
11391          */
11392         actionfailed : true,
11393         /**
11394          * @event actioncomplete
11395          * Fires when an action is completed.
11396          * @param {Form} this
11397          * @param {Action} action The action that completed
11398          */
11399         actioncomplete : true
11400     });
11401 };
11402
11403 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11404
11405      /**
11406      * @cfg {String} method
11407      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11408      */
11409     method : 'POST',
11410     /**
11411      * @cfg {String} url
11412      * The URL to use for form actions if one isn't supplied in the action options.
11413      */
11414     /**
11415      * @cfg {Boolean} fileUpload
11416      * Set to true if this form is a file upload.
11417      */
11418
11419     /**
11420      * @cfg {Object} baseParams
11421      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11422      */
11423
11424     /**
11425      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11426      */
11427     timeout: 30,
11428     /**
11429      * @cfg {Sting} align (left|right) for navbar forms
11430      */
11431     align : 'left',
11432
11433     // private
11434     activeAction : null,
11435
11436     /**
11437      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11438      * element by passing it or its id or mask the form itself by passing in true.
11439      * @type Mixed
11440      */
11441     waitMsgTarget : false,
11442
11443     loadMask : true,
11444     
11445     /**
11446      * @cfg {Boolean} errorMask (true|false) default false
11447      */
11448     errorMask : false,
11449     
11450     /**
11451      * @cfg {Number} maskOffset Default 100
11452      */
11453     maskOffset : 100,
11454     
11455     /**
11456      * @cfg {Boolean} maskBody
11457      */
11458     maskBody : false,
11459
11460     getAutoCreate : function(){
11461
11462         var cfg = {
11463             tag: 'form',
11464             method : this.method || 'POST',
11465             id : this.id || Roo.id(),
11466             cls : ''
11467         };
11468         if (this.parent().xtype.match(/^Nav/)) {
11469             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11470
11471         }
11472
11473         if (this.labelAlign == 'left' ) {
11474             cfg.cls += ' form-horizontal';
11475         }
11476
11477
11478         return cfg;
11479     },
11480     initEvents : function()
11481     {
11482         this.el.on('submit', this.onSubmit, this);
11483         // this was added as random key presses on the form where triggering form submit.
11484         this.el.on('keypress', function(e) {
11485             if (e.getCharCode() != 13) {
11486                 return true;
11487             }
11488             // we might need to allow it for textareas.. and some other items.
11489             // check e.getTarget().
11490
11491             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11492                 return true;
11493             }
11494
11495             Roo.log("keypress blocked");
11496
11497             e.preventDefault();
11498             return false;
11499         });
11500         
11501     },
11502     // private
11503     onSubmit : function(e){
11504         e.stopEvent();
11505     },
11506
11507      /**
11508      * Returns true if client-side validation on the form is successful.
11509      * @return Boolean
11510      */
11511     isValid : function(){
11512         var items = this.getItems();
11513         var valid = true;
11514         var target = false;
11515         
11516         items.each(function(f){
11517             
11518             if(f.validate()){
11519                 return;
11520             }
11521             
11522             Roo.log('invalid field: ' + f.name);
11523             
11524             valid = false;
11525
11526             if(!target && f.el.isVisible(true)){
11527                 target = f;
11528             }
11529            
11530         });
11531         
11532         if(this.errorMask && !valid){
11533             Roo.bootstrap.form.Form.popover.mask(this, target);
11534         }
11535         
11536         return valid;
11537     },
11538     
11539     /**
11540      * Returns true if any fields in this form have changed since their original load.
11541      * @return Boolean
11542      */
11543     isDirty : function(){
11544         var dirty = false;
11545         var items = this.getItems();
11546         items.each(function(f){
11547            if(f.isDirty()){
11548                dirty = true;
11549                return false;
11550            }
11551            return true;
11552         });
11553         return dirty;
11554     },
11555      /**
11556      * Performs a predefined action (submit or load) or custom actions you define on this form.
11557      * @param {String} actionName The name of the action type
11558      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11559      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11560      * accept other config options):
11561      * <pre>
11562 Property          Type             Description
11563 ----------------  ---------------  ----------------------------------------------------------------------------------
11564 url               String           The url for the action (defaults to the form's url)
11565 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11566 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11567 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11568                                    validate the form on the client (defaults to false)
11569      * </pre>
11570      * @return {BasicForm} this
11571      */
11572     doAction : function(action, options){
11573         if(typeof action == 'string'){
11574             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11575         }
11576         if(this.fireEvent('beforeaction', this, action) !== false){
11577             this.beforeAction(action);
11578             action.run.defer(100, action);
11579         }
11580         return this;
11581     },
11582
11583     // private
11584     beforeAction : function(action){
11585         var o = action.options;
11586         
11587         if(this.loadMask){
11588             
11589             if(this.maskBody){
11590                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11591             } else {
11592                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11593             }
11594         }
11595         // not really supported yet.. ??
11596
11597         //if(this.waitMsgTarget === true){
11598         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11599         //}else if(this.waitMsgTarget){
11600         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11601         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11602         //}else {
11603         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11604        // }
11605
11606     },
11607
11608     // private
11609     afterAction : function(action, success){
11610         this.activeAction = null;
11611         var o = action.options;
11612
11613         if(this.loadMask){
11614             
11615             if(this.maskBody){
11616                 Roo.get(document.body).unmask();
11617             } else {
11618                 this.el.unmask();
11619             }
11620         }
11621         
11622         //if(this.waitMsgTarget === true){
11623 //            this.el.unmask();
11624         //}else if(this.waitMsgTarget){
11625         //    this.waitMsgTarget.unmask();
11626         //}else{
11627         //    Roo.MessageBox.updateProgress(1);
11628         //    Roo.MessageBox.hide();
11629        // }
11630         //
11631         if(success){
11632             if(o.reset){
11633                 this.reset();
11634             }
11635             Roo.callback(o.success, o.scope, [this, action]);
11636             this.fireEvent('actioncomplete', this, action);
11637
11638         }else{
11639
11640             // failure condition..
11641             // we have a scenario where updates need confirming.
11642             // eg. if a locking scenario exists..
11643             // we look for { errors : { needs_confirm : true }} in the response.
11644             if (
11645                 (typeof(action.result) != 'undefined')  &&
11646                 (typeof(action.result.errors) != 'undefined')  &&
11647                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11648            ){
11649                 var _t = this;
11650                 Roo.log("not supported yet");
11651                  /*
11652
11653                 Roo.MessageBox.confirm(
11654                     "Change requires confirmation",
11655                     action.result.errorMsg,
11656                     function(r) {
11657                         if (r != 'yes') {
11658                             return;
11659                         }
11660                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11661                     }
11662
11663                 );
11664                 */
11665
11666
11667                 return;
11668             }
11669
11670             Roo.callback(o.failure, o.scope, [this, action]);
11671             // show an error message if no failed handler is set..
11672             if (!this.hasListener('actionfailed')) {
11673                 Roo.log("need to add dialog support");
11674                 /*
11675                 Roo.MessageBox.alert("Error",
11676                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11677                         action.result.errorMsg :
11678                         "Saving Failed, please check your entries or try again"
11679                 );
11680                 */
11681             }
11682
11683             this.fireEvent('actionfailed', this, action);
11684         }
11685
11686     },
11687     /**
11688      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11689      * @param {String} id The value to search for
11690      * @return Field
11691      */
11692     findField : function(id){
11693         var items = this.getItems();
11694         var field = items.get(id);
11695         if(!field){
11696              items.each(function(f){
11697                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11698                     field = f;
11699                     return false;
11700                 }
11701                 return true;
11702             });
11703         }
11704         return field || null;
11705     },
11706      /**
11707      * Mark fields in this form invalid in bulk.
11708      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11709      * @return {BasicForm} this
11710      */
11711     markInvalid : function(errors){
11712         if(errors instanceof Array){
11713             for(var i = 0, len = errors.length; i < len; i++){
11714                 var fieldError = errors[i];
11715                 var f = this.findField(fieldError.id);
11716                 if(f){
11717                     f.markInvalid(fieldError.msg);
11718                 }
11719             }
11720         }else{
11721             var field, id;
11722             for(id in errors){
11723                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11724                     field.markInvalid(errors[id]);
11725                 }
11726             }
11727         }
11728         //Roo.each(this.childForms || [], function (f) {
11729         //    f.markInvalid(errors);
11730         //});
11731
11732         return this;
11733     },
11734
11735     /**
11736      * Set values for fields in this form in bulk.
11737      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11738      * @return {BasicForm} this
11739      */
11740     setValues : function(values){
11741         if(values instanceof Array){ // array of objects
11742             for(var i = 0, len = values.length; i < len; i++){
11743                 var v = values[i];
11744                 var f = this.findField(v.id);
11745                 if(f){
11746                     f.setValue(v.value);
11747                     if(this.trackResetOnLoad){
11748                         f.originalValue = f.getValue();
11749                     }
11750                 }
11751             }
11752         }else{ // object hash
11753             var field, id;
11754             for(id in values){
11755                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11756
11757                     if (field.setFromData &&
11758                         field.valueField &&
11759                         field.displayField &&
11760                         // combos' with local stores can
11761                         // be queried via setValue()
11762                         // to set their value..
11763                         (field.store && !field.store.isLocal)
11764                         ) {
11765                         // it's a combo
11766                         var sd = { };
11767                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11768                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11769                         field.setFromData(sd);
11770
11771                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11772                         
11773                         field.setFromData(values);
11774                         
11775                     } else {
11776                         field.setValue(values[id]);
11777                     }
11778
11779
11780                     if(this.trackResetOnLoad){
11781                         field.originalValue = field.getValue();
11782                     }
11783                 }
11784             }
11785         }
11786
11787         //Roo.each(this.childForms || [], function (f) {
11788         //    f.setValues(values);
11789         //});
11790
11791         return this;
11792     },
11793
11794     /**
11795      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11796      * they are returned as an array.
11797      * @param {Boolean} asString
11798      * @return {Object}
11799      */
11800     getValues : function(asString){
11801         //if (this.childForms) {
11802             // copy values from the child forms
11803         //    Roo.each(this.childForms, function (f) {
11804         //        this.setValues(f.getValues());
11805         //    }, this);
11806         //}
11807
11808
11809
11810         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11811         if(asString === true){
11812             return fs;
11813         }
11814         return Roo.urlDecode(fs);
11815     },
11816
11817     /**
11818      * Returns the fields in this form as an object with key/value pairs.
11819      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11820      * @return {Object}
11821      */
11822     getFieldValues : function(with_hidden)
11823     {
11824         var items = this.getItems();
11825         var ret = {};
11826         items.each(function(f){
11827             
11828             if (!f.getName()) {
11829                 return;
11830             }
11831             
11832             var v = f.getValue();
11833             
11834             if (f.inputType =='radio') {
11835                 if (typeof(ret[f.getName()]) == 'undefined') {
11836                     ret[f.getName()] = ''; // empty..
11837                 }
11838
11839                 if (!f.el.dom.checked) {
11840                     return;
11841
11842                 }
11843                 v = f.el.dom.value;
11844
11845             }
11846             
11847             if(f.xtype == 'MoneyField'){
11848                 ret[f.currencyName] = f.getCurrency();
11849             }
11850
11851             // not sure if this supported any more..
11852             if ((typeof(v) == 'object') && f.getRawValue) {
11853                 v = f.getRawValue() ; // dates..
11854             }
11855             // combo boxes where name != hiddenName...
11856             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11857                 ret[f.name] = f.getRawValue();
11858             }
11859             ret[f.getName()] = v;
11860         });
11861
11862         return ret;
11863     },
11864
11865     /**
11866      * Clears all invalid messages in this form.
11867      * @return {BasicForm} this
11868      */
11869     clearInvalid : function(){
11870         var items = this.getItems();
11871
11872         items.each(function(f){
11873            f.clearInvalid();
11874         });
11875
11876         return this;
11877     },
11878
11879     /**
11880      * Resets this form.
11881      * @return {BasicForm} this
11882      */
11883     reset : function(){
11884         var items = this.getItems();
11885         items.each(function(f){
11886             f.reset();
11887         });
11888
11889         Roo.each(this.childForms || [], function (f) {
11890             f.reset();
11891         });
11892
11893
11894         return this;
11895     },
11896     
11897     getItems : function()
11898     {
11899         var r=new Roo.util.MixedCollection(false, function(o){
11900             return o.id || (o.id = Roo.id());
11901         });
11902         var iter = function(el) {
11903             if (el.inputEl) {
11904                 r.add(el);
11905             }
11906             if (!el.items) {
11907                 return;
11908             }
11909             Roo.each(el.items,function(e) {
11910                 iter(e);
11911             });
11912         };
11913
11914         iter(this);
11915         return r;
11916     },
11917     
11918     hideFields : function(items)
11919     {
11920         Roo.each(items, function(i){
11921             
11922             var f = this.findField(i);
11923             
11924             if(!f){
11925                 return;
11926             }
11927             
11928             f.hide();
11929             
11930         }, this);
11931     },
11932     
11933     showFields : function(items)
11934     {
11935         Roo.each(items, function(i){
11936             
11937             var f = this.findField(i);
11938             
11939             if(!f){
11940                 return;
11941             }
11942             
11943             f.show();
11944             
11945         }, this);
11946     }
11947
11948 });
11949
11950 Roo.apply(Roo.bootstrap.form.Form, {
11951     
11952     popover : {
11953         
11954         padding : 5,
11955         
11956         isApplied : false,
11957         
11958         isMasked : false,
11959         
11960         form : false,
11961         
11962         target : false,
11963         
11964         toolTip : false,
11965         
11966         intervalID : false,
11967         
11968         maskEl : false,
11969         
11970         apply : function()
11971         {
11972             if(this.isApplied){
11973                 return;
11974             }
11975             
11976             this.maskEl = {
11977                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11978                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11979                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11980                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11981             };
11982             
11983             this.maskEl.top.enableDisplayMode("block");
11984             this.maskEl.left.enableDisplayMode("block");
11985             this.maskEl.bottom.enableDisplayMode("block");
11986             this.maskEl.right.enableDisplayMode("block");
11987             
11988             this.toolTip = new Roo.bootstrap.Tooltip({
11989                 cls : 'roo-form-error-popover',
11990                 alignment : {
11991                     'left' : ['r-l', [-2,0], 'right'],
11992                     'right' : ['l-r', [2,0], 'left'],
11993                     'bottom' : ['tl-bl', [0,2], 'top'],
11994                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11995                 }
11996             });
11997             
11998             this.toolTip.render(Roo.get(document.body));
11999
12000             this.toolTip.el.enableDisplayMode("block");
12001             
12002             Roo.get(document.body).on('click', function(){
12003                 this.unmask();
12004             }, this);
12005             
12006             Roo.get(document.body).on('touchstart', function(){
12007                 this.unmask();
12008             }, this);
12009             
12010             this.isApplied = true
12011         },
12012         
12013         mask : function(form, target)
12014         {
12015             this.form = form;
12016             
12017             this.target = target;
12018             
12019             if(!this.form.errorMask || !target.el){
12020                 return;
12021             }
12022             
12023             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12024             
12025             Roo.log(scrollable);
12026             
12027             var ot = this.target.el.calcOffsetsTo(scrollable);
12028             
12029             var scrollTo = ot[1] - this.form.maskOffset;
12030             
12031             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12032             
12033             scrollable.scrollTo('top', scrollTo);
12034             
12035             var box = this.target.el.getBox();
12036             Roo.log(box);
12037             var zIndex = Roo.bootstrap.Modal.zIndex++;
12038
12039             
12040             this.maskEl.top.setStyle('position', 'absolute');
12041             this.maskEl.top.setStyle('z-index', zIndex);
12042             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12043             this.maskEl.top.setLeft(0);
12044             this.maskEl.top.setTop(0);
12045             this.maskEl.top.show();
12046             
12047             this.maskEl.left.setStyle('position', 'absolute');
12048             this.maskEl.left.setStyle('z-index', zIndex);
12049             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12050             this.maskEl.left.setLeft(0);
12051             this.maskEl.left.setTop(box.y - this.padding);
12052             this.maskEl.left.show();
12053
12054             this.maskEl.bottom.setStyle('position', 'absolute');
12055             this.maskEl.bottom.setStyle('z-index', zIndex);
12056             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12057             this.maskEl.bottom.setLeft(0);
12058             this.maskEl.bottom.setTop(box.bottom + this.padding);
12059             this.maskEl.bottom.show();
12060
12061             this.maskEl.right.setStyle('position', 'absolute');
12062             this.maskEl.right.setStyle('z-index', zIndex);
12063             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12064             this.maskEl.right.setLeft(box.right + this.padding);
12065             this.maskEl.right.setTop(box.y - this.padding);
12066             this.maskEl.right.show();
12067
12068             this.toolTip.bindEl = this.target.el;
12069
12070             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12071
12072             var tip = this.target.blankText;
12073
12074             if(this.target.getValue() !== '' ) {
12075                 
12076                 if (this.target.invalidText.length) {
12077                     tip = this.target.invalidText;
12078                 } else if (this.target.regexText.length){
12079                     tip = this.target.regexText;
12080                 }
12081             }
12082
12083             this.toolTip.show(tip);
12084
12085             this.intervalID = window.setInterval(function() {
12086                 Roo.bootstrap.form.Form.popover.unmask();
12087             }, 10000);
12088
12089             window.onwheel = function(){ return false;};
12090             
12091             (function(){ this.isMasked = true; }).defer(500, this);
12092             
12093         },
12094         
12095         unmask : function()
12096         {
12097             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12098                 return;
12099             }
12100             
12101             this.maskEl.top.setStyle('position', 'absolute');
12102             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12103             this.maskEl.top.hide();
12104
12105             this.maskEl.left.setStyle('position', 'absolute');
12106             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12107             this.maskEl.left.hide();
12108
12109             this.maskEl.bottom.setStyle('position', 'absolute');
12110             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12111             this.maskEl.bottom.hide();
12112
12113             this.maskEl.right.setStyle('position', 'absolute');
12114             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12115             this.maskEl.right.hide();
12116             
12117             this.toolTip.hide();
12118             
12119             this.toolTip.el.hide();
12120             
12121             window.onwheel = function(){ return true;};
12122             
12123             if(this.intervalID){
12124                 window.clearInterval(this.intervalID);
12125                 this.intervalID = false;
12126             }
12127             
12128             this.isMasked = false;
12129             
12130         }
12131         
12132     }
12133     
12134 });
12135
12136 /*
12137  * Based on:
12138  * Ext JS Library 1.1.1
12139  * Copyright(c) 2006-2007, Ext JS, LLC.
12140  *
12141  * Originally Released Under LGPL - original licence link has changed is not relivant.
12142  *
12143  * Fork - LGPL
12144  * <script type="text/javascript">
12145  */
12146 /**
12147  * @class Roo.form.VTypes
12148  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12149  * @static
12150  */
12151 Roo.form.VTypes = function(){
12152     // closure these in so they are only created once.
12153     var alpha = /^[a-zA-Z_]+$/;
12154     var alphanum = /^[a-zA-Z0-9_]+$/;
12155     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12156     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12157
12158     // All these messages and functions are configurable
12159     return {
12160         /**
12161          * The function used to validate email addresses
12162          * @param {String} value The email address
12163          */
12164         'email' : function(v){
12165             return email.test(v);
12166         },
12167         /**
12168          * The error text to display when the email validation function returns false
12169          * @type String
12170          */
12171         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12172         /**
12173          * The keystroke filter mask to be applied on email input
12174          * @type RegExp
12175          */
12176         'emailMask' : /[a-z0-9_\.\-@]/i,
12177
12178         /**
12179          * The function used to validate URLs
12180          * @param {String} value The URL
12181          */
12182         'url' : function(v){
12183             return url.test(v);
12184         },
12185         /**
12186          * The error text to display when the url validation function returns false
12187          * @type String
12188          */
12189         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12190         
12191         /**
12192          * The function used to validate alpha values
12193          * @param {String} value The value
12194          */
12195         'alpha' : function(v){
12196             return alpha.test(v);
12197         },
12198         /**
12199          * The error text to display when the alpha validation function returns false
12200          * @type String
12201          */
12202         'alphaText' : 'This field should only contain letters and _',
12203         /**
12204          * The keystroke filter mask to be applied on alpha input
12205          * @type RegExp
12206          */
12207         'alphaMask' : /[a-z_]/i,
12208
12209         /**
12210          * The function used to validate alphanumeric values
12211          * @param {String} value The value
12212          */
12213         'alphanum' : function(v){
12214             return alphanum.test(v);
12215         },
12216         /**
12217          * The error text to display when the alphanumeric validation function returns false
12218          * @type String
12219          */
12220         'alphanumText' : 'This field should only contain letters, numbers and _',
12221         /**
12222          * The keystroke filter mask to be applied on alphanumeric input
12223          * @type RegExp
12224          */
12225         'alphanumMask' : /[a-z0-9_]/i
12226     };
12227 }();/*
12228  * - LGPL
12229  *
12230  * Input
12231  * 
12232  */
12233
12234 /**
12235  * @class Roo.bootstrap.form.Input
12236  * @extends Roo.bootstrap.Component
12237  * Bootstrap Input class
12238  * @cfg {Boolean} disabled is it disabled
12239  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12240  * @cfg {String} name name of the input
12241  * @cfg {string} fieldLabel - the label associated
12242  * @cfg {string} placeholder - placeholder to put in text.
12243  * @cfg {string} before - input group add on before
12244  * @cfg {string} after - input group add on after
12245  * @cfg {string} size - (lg|sm) or leave empty..
12246  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12247  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12248  * @cfg {Number} md colspan out of 12 for computer-sized screens
12249  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12250  * @cfg {string} value default value of the input
12251  * @cfg {Number} labelWidth set the width of label 
12252  * @cfg {Number} labellg set the width of label (1-12)
12253  * @cfg {Number} labelmd set the width of label (1-12)
12254  * @cfg {Number} labelsm set the width of label (1-12)
12255  * @cfg {Number} labelxs set the width of label (1-12)
12256  * @cfg {String} labelAlign (top|left)
12257  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12258  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12259  * @cfg {String} indicatorpos (left|right) default left
12260  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12261  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12262  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12263  * @cfg {Roo.bootstrap.Button} before Button to show before
12264  * @cfg {Roo.bootstrap.Button} afterButton to show before
12265  * @cfg {String} align (left|center|right) Default left
12266  * @cfg {Boolean} forceFeedback (true|false) Default false
12267  * 
12268  * @constructor
12269  * Create a new Input
12270  * @param {Object} config The config object
12271  */
12272
12273 Roo.bootstrap.form.Input = function(config){
12274     
12275     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12276     
12277     this.addEvents({
12278         /**
12279          * @event focus
12280          * Fires when this field receives input focus.
12281          * @param {Roo.form.Field} this
12282          */
12283         focus : true,
12284         /**
12285          * @event blur
12286          * Fires when this field loses input focus.
12287          * @param {Roo.form.Field} this
12288          */
12289         blur : true,
12290         /**
12291          * @event specialkey
12292          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12293          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12294          * @param {Roo.form.Field} this
12295          * @param {Roo.EventObject} e The event object
12296          */
12297         specialkey : true,
12298         /**
12299          * @event change
12300          * Fires just before the field blurs if the field value has changed.
12301          * @param {Roo.form.Field} this
12302          * @param {Mixed} newValue The new value
12303          * @param {Mixed} oldValue The original value
12304          */
12305         change : true,
12306         /**
12307          * @event invalid
12308          * Fires after the field has been marked as invalid.
12309          * @param {Roo.form.Field} this
12310          * @param {String} msg The validation message
12311          */
12312         invalid : true,
12313         /**
12314          * @event valid
12315          * Fires after the field has been validated with no errors.
12316          * @param {Roo.form.Field} this
12317          */
12318         valid : true,
12319          /**
12320          * @event keyup
12321          * Fires after the key up
12322          * @param {Roo.form.Field} this
12323          * @param {Roo.EventObject}  e The event Object
12324          */
12325         keyup : true,
12326         /**
12327          * @event paste
12328          * Fires after the user pastes into input
12329          * @param {Roo.form.Field} this
12330          * @param {Roo.EventObject}  e The event Object
12331          */
12332         paste : true
12333     });
12334 };
12335
12336 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12337      /**
12338      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12339       automatic validation (defaults to "keyup").
12340      */
12341     validationEvent : "keyup",
12342      /**
12343      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12344      */
12345     validateOnBlur : true,
12346     /**
12347      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12348      */
12349     validationDelay : 250,
12350      /**
12351      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12352      */
12353     focusClass : "x-form-focus",  // not needed???
12354     
12355        
12356     /**
12357      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12358      */
12359     invalidClass : "has-warning",
12360     
12361     /**
12362      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12363      */
12364     validClass : "has-success",
12365     
12366     /**
12367      * @cfg {Boolean} hasFeedback (true|false) default true
12368      */
12369     hasFeedback : true,
12370     
12371     /**
12372      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12373      */
12374     invalidFeedbackClass : "glyphicon-warning-sign",
12375     
12376     /**
12377      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12378      */
12379     validFeedbackClass : "glyphicon-ok",
12380     
12381     /**
12382      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12383      */
12384     selectOnFocus : false,
12385     
12386      /**
12387      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12388      */
12389     maskRe : null,
12390        /**
12391      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12392      */
12393     vtype : null,
12394     
12395       /**
12396      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12397      */
12398     disableKeyFilter : false,
12399     
12400        /**
12401      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12402      */
12403     disabled : false,
12404      /**
12405      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12406      */
12407     allowBlank : true,
12408     /**
12409      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12410      */
12411     blankText : "Please complete this mandatory field",
12412     
12413      /**
12414      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12415      */
12416     minLength : 0,
12417     /**
12418      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12419      */
12420     maxLength : Number.MAX_VALUE,
12421     /**
12422      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12423      */
12424     minLengthText : "The minimum length for this field is {0}",
12425     /**
12426      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12427      */
12428     maxLengthText : "The maximum length for this field is {0}",
12429   
12430     
12431     /**
12432      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12433      * If available, this function will be called only after the basic validators all return true, and will be passed the
12434      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12435      */
12436     validator : null,
12437     /**
12438      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12439      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12440      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12441      */
12442     regex : null,
12443     /**
12444      * @cfg {String} regexText -- Depricated - use Invalid Text
12445      */
12446     regexText : "",
12447     
12448     /**
12449      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12450      */
12451     invalidText : "",
12452     
12453     
12454     
12455     autocomplete: false,
12456     
12457     
12458     fieldLabel : '',
12459     inputType : 'text',
12460     
12461     name : false,
12462     placeholder: false,
12463     before : false,
12464     after : false,
12465     size : false,
12466     hasFocus : false,
12467     preventMark: false,
12468     isFormField : true,
12469     value : '',
12470     labelWidth : 2,
12471     labelAlign : false,
12472     readOnly : false,
12473     align : false,
12474     formatedValue : false,
12475     forceFeedback : false,
12476     
12477     indicatorpos : 'left',
12478     
12479     labellg : 0,
12480     labelmd : 0,
12481     labelsm : 0,
12482     labelxs : 0,
12483     
12484     capture : '',
12485     accept : '',
12486     
12487     parentLabelAlign : function()
12488     {
12489         var parent = this;
12490         while (parent.parent()) {
12491             parent = parent.parent();
12492             if (typeof(parent.labelAlign) !='undefined') {
12493                 return parent.labelAlign;
12494             }
12495         }
12496         return 'left';
12497         
12498     },
12499     
12500     getAutoCreate : function()
12501     {
12502         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12503         
12504         var id = Roo.id();
12505         
12506         var cfg = {};
12507         
12508         if(this.inputType != 'hidden'){
12509             cfg.cls = 'form-group' //input-group
12510         }
12511         
12512         var input =  {
12513             tag: 'input',
12514             id : id,
12515             type : this.inputType,
12516             value : this.value,
12517             cls : 'form-control',
12518             placeholder : this.placeholder || '',
12519             autocomplete : this.autocomplete || 'new-password'
12520         };
12521         if (this.inputType == 'file') {
12522             input.style = 'overflow:hidden'; // why not in CSS?
12523         }
12524         
12525         if(this.capture.length){
12526             input.capture = this.capture;
12527         }
12528         
12529         if(this.accept.length){
12530             input.accept = this.accept + "/*";
12531         }
12532         
12533         if(this.align){
12534             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12535         }
12536         
12537         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12538             input.maxLength = this.maxLength;
12539         }
12540         
12541         if (this.disabled) {
12542             input.disabled=true;
12543         }
12544         
12545         if (this.readOnly) {
12546             input.readonly=true;
12547         }
12548         
12549         if (this.name) {
12550             input.name = this.name;
12551         }
12552         
12553         if (this.size) {
12554             input.cls += ' input-' + this.size;
12555         }
12556         
12557         var settings=this;
12558         ['xs','sm','md','lg'].map(function(size){
12559             if (settings[size]) {
12560                 cfg.cls += ' col-' + size + '-' + settings[size];
12561             }
12562         });
12563         
12564         var inputblock = input;
12565         
12566         var feedback = {
12567             tag: 'span',
12568             cls: 'glyphicon form-control-feedback'
12569         };
12570             
12571         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12572             
12573             inputblock = {
12574                 cls : 'has-feedback',
12575                 cn :  [
12576                     input,
12577                     feedback
12578                 ] 
12579             };  
12580         }
12581         
12582         if (this.before || this.after) {
12583             
12584             inputblock = {
12585                 cls : 'input-group',
12586                 cn :  [] 
12587             };
12588             
12589             if (this.before && typeof(this.before) == 'string') {
12590                 
12591                 inputblock.cn.push({
12592                     tag :'span',
12593                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12594                     html : this.before
12595                 });
12596             }
12597             if (this.before && typeof(this.before) == 'object') {
12598                 this.before = Roo.factory(this.before);
12599                 
12600                 inputblock.cn.push({
12601                     tag :'span',
12602                     cls : 'roo-input-before input-group-prepend   input-group-' +
12603                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12604                 });
12605             }
12606             
12607             inputblock.cn.push(input);
12608             
12609             if (this.after && typeof(this.after) == 'string') {
12610                 inputblock.cn.push({
12611                     tag :'span',
12612                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12613                     html : this.after
12614                 });
12615             }
12616             if (this.after && typeof(this.after) == 'object') {
12617                 this.after = Roo.factory(this.after);
12618                 
12619                 inputblock.cn.push({
12620                     tag :'span',
12621                     cls : 'roo-input-after input-group-append  input-group-' +
12622                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12623                 });
12624             }
12625             
12626             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12627                 inputblock.cls += ' has-feedback';
12628                 inputblock.cn.push(feedback);
12629             }
12630         };
12631         var indicator = {
12632             tag : 'i',
12633             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12634             tooltip : 'This field is required'
12635         };
12636         if (this.allowBlank ) {
12637             indicator.style = this.allowBlank ? ' display:none' : '';
12638         }
12639         if (align ==='left' && this.fieldLabel.length) {
12640             
12641             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12642             
12643             cfg.cn = [
12644                 indicator,
12645                 {
12646                     tag: 'label',
12647                     'for' :  id,
12648                     cls : 'control-label col-form-label',
12649                     html : this.fieldLabel
12650
12651                 },
12652                 {
12653                     cls : "", 
12654                     cn: [
12655                         inputblock
12656                     ]
12657                 }
12658             ];
12659             
12660             var labelCfg = cfg.cn[1];
12661             var contentCfg = cfg.cn[2];
12662             
12663             if(this.indicatorpos == 'right'){
12664                 cfg.cn = [
12665                     {
12666                         tag: 'label',
12667                         'for' :  id,
12668                         cls : 'control-label col-form-label',
12669                         cn : [
12670                             {
12671                                 tag : 'span',
12672                                 html : this.fieldLabel
12673                             },
12674                             indicator
12675                         ]
12676                     },
12677                     {
12678                         cls : "",
12679                         cn: [
12680                             inputblock
12681                         ]
12682                     }
12683
12684                 ];
12685                 
12686                 labelCfg = cfg.cn[0];
12687                 contentCfg = cfg.cn[1];
12688             
12689             }
12690             
12691             if(this.labelWidth > 12){
12692                 labelCfg.style = "width: " + this.labelWidth + 'px';
12693             }
12694             
12695             if(this.labelWidth < 13 && this.labelmd == 0){
12696                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12697             }
12698             
12699             if(this.labellg > 0){
12700                 labelCfg.cls += ' col-lg-' + this.labellg;
12701                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12702             }
12703             
12704             if(this.labelmd > 0){
12705                 labelCfg.cls += ' col-md-' + this.labelmd;
12706                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12707             }
12708             
12709             if(this.labelsm > 0){
12710                 labelCfg.cls += ' col-sm-' + this.labelsm;
12711                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12712             }
12713             
12714             if(this.labelxs > 0){
12715                 labelCfg.cls += ' col-xs-' + this.labelxs;
12716                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12717             }
12718             
12719             
12720         } else if ( this.fieldLabel.length) {
12721                 
12722             
12723             
12724             cfg.cn = [
12725                 {
12726                     tag : 'i',
12727                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12728                     tooltip : 'This field is required',
12729                     style : this.allowBlank ? ' display:none' : '' 
12730                 },
12731                 {
12732                     tag: 'label',
12733                    //cls : 'input-group-addon',
12734                     html : this.fieldLabel
12735
12736                 },
12737
12738                inputblock
12739
12740            ];
12741            
12742            if(this.indicatorpos == 'right'){
12743        
12744                 cfg.cn = [
12745                     {
12746                         tag: 'label',
12747                        //cls : 'input-group-addon',
12748                         html : this.fieldLabel
12749
12750                     },
12751                     {
12752                         tag : 'i',
12753                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12754                         tooltip : 'This field is required',
12755                         style : this.allowBlank ? ' display:none' : '' 
12756                     },
12757
12758                    inputblock
12759
12760                ];
12761
12762             }
12763
12764         } else {
12765             
12766             cfg.cn = [
12767
12768                     inputblock
12769
12770             ];
12771                 
12772                 
12773         };
12774         
12775         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12776            cfg.cls += ' navbar-form';
12777         }
12778         
12779         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12780             // on BS4 we do this only if not form 
12781             cfg.cls += ' navbar-form';
12782             cfg.tag = 'li';
12783         }
12784         
12785         return cfg;
12786         
12787     },
12788     /**
12789      * return the real input element.
12790      */
12791     inputEl: function ()
12792     {
12793         return this.el.select('input.form-control',true).first();
12794     },
12795     
12796     tooltipEl : function()
12797     {
12798         return this.inputEl();
12799     },
12800     
12801     indicatorEl : function()
12802     {
12803         if (Roo.bootstrap.version == 4) {
12804             return false; // not enabled in v4 yet.
12805         }
12806         
12807         var indicator = this.el.select('i.roo-required-indicator',true).first();
12808         
12809         if(!indicator){
12810             return false;
12811         }
12812         
12813         return indicator;
12814         
12815     },
12816     
12817     setDisabled : function(v)
12818     {
12819         var i  = this.inputEl().dom;
12820         if (!v) {
12821             i.removeAttribute('disabled');
12822             return;
12823             
12824         }
12825         i.setAttribute('disabled','true');
12826     },
12827     initEvents : function()
12828     {
12829           
12830         this.inputEl().on("keydown" , this.fireKey,  this);
12831         this.inputEl().on("focus", this.onFocus,  this);
12832         this.inputEl().on("blur", this.onBlur,  this);
12833         
12834         this.inputEl().relayEvent('keyup', this);
12835         this.inputEl().relayEvent('paste', this);
12836         
12837         this.indicator = this.indicatorEl();
12838         
12839         if(this.indicator){
12840             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12841         }
12842  
12843         // reference to original value for reset
12844         this.originalValue = this.getValue();
12845         //Roo.form.TextField.superclass.initEvents.call(this);
12846         if(this.validationEvent == 'keyup'){
12847             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12848             this.inputEl().on('keyup', this.filterValidation, this);
12849         }
12850         else if(this.validationEvent !== false){
12851             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12852         }
12853         
12854         if(this.selectOnFocus){
12855             this.on("focus", this.preFocus, this);
12856             
12857         }
12858         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12859             this.inputEl().on("keypress", this.filterKeys, this);
12860         } else {
12861             this.inputEl().relayEvent('keypress', this);
12862         }
12863        /* if(this.grow){
12864             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12865             this.el.on("click", this.autoSize,  this);
12866         }
12867         */
12868         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12869             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12870         }
12871         
12872         if (typeof(this.before) == 'object') {
12873             this.before.render(this.el.select('.roo-input-before',true).first());
12874         }
12875         if (typeof(this.after) == 'object') {
12876             this.after.render(this.el.select('.roo-input-after',true).first());
12877         }
12878         
12879         this.inputEl().on('change', this.onChange, this);
12880         
12881     },
12882     filterValidation : function(e){
12883         if(!e.isNavKeyPress()){
12884             this.validationTask.delay(this.validationDelay);
12885         }
12886     },
12887      /**
12888      * Validates the field value
12889      * @return {Boolean} True if the value is valid, else false
12890      */
12891     validate : function(){
12892         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12893         if(this.disabled || this.validateValue(this.getRawValue())){
12894             this.markValid();
12895             return true;
12896         }
12897         
12898         this.markInvalid();
12899         return false;
12900     },
12901     
12902     
12903     /**
12904      * Validates a value according to the field's validation rules and marks the field as invalid
12905      * if the validation fails
12906      * @param {Mixed} value The value to validate
12907      * @return {Boolean} True if the value is valid, else false
12908      */
12909     validateValue : function(value)
12910     {
12911         if(this.getVisibilityEl().hasClass('hidden')){
12912             return true;
12913         }
12914         
12915         if(value.length < 1)  { // if it's blank
12916             if(this.allowBlank){
12917                 return true;
12918             }
12919             return false;
12920         }
12921         
12922         if(value.length < this.minLength){
12923             return false;
12924         }
12925         if(value.length > this.maxLength){
12926             return false;
12927         }
12928         if(this.vtype){
12929             var vt = Roo.form.VTypes;
12930             if(!vt[this.vtype](value, this)){
12931                 return false;
12932             }
12933         }
12934         if(typeof this.validator == "function"){
12935             var msg = this.validator(value);
12936             if(msg !== true){
12937                 return false;
12938             }
12939             if (typeof(msg) == 'string') {
12940                 this.invalidText = msg;
12941             }
12942         }
12943         
12944         if(this.regex && !this.regex.test(value)){
12945             return false;
12946         }
12947         
12948         return true;
12949     },
12950     
12951      // private
12952     fireKey : function(e){
12953         //Roo.log('field ' + e.getKey());
12954         if(e.isNavKeyPress()){
12955             this.fireEvent("specialkey", this, e);
12956         }
12957     },
12958     focus : function (selectText){
12959         if(this.rendered){
12960             this.inputEl().focus();
12961             if(selectText === true){
12962                 this.inputEl().dom.select();
12963             }
12964         }
12965         return this;
12966     } ,
12967     
12968     onFocus : function(){
12969         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12970            // this.el.addClass(this.focusClass);
12971         }
12972         if(!this.hasFocus){
12973             this.hasFocus = true;
12974             this.startValue = this.getValue();
12975             this.fireEvent("focus", this);
12976         }
12977     },
12978     
12979     beforeBlur : Roo.emptyFn,
12980
12981     
12982     // private
12983     onBlur : function(){
12984         this.beforeBlur();
12985         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12986             //this.el.removeClass(this.focusClass);
12987         }
12988         this.hasFocus = false;
12989         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12990             this.validate();
12991         }
12992         var v = this.getValue();
12993         if(String(v) !== String(this.startValue)){
12994             this.fireEvent('change', this, v, this.startValue);
12995         }
12996         this.fireEvent("blur", this);
12997     },
12998     
12999     onChange : function(e)
13000     {
13001         var v = this.getValue();
13002         if(String(v) !== String(this.startValue)){
13003             this.fireEvent('change', this, v, this.startValue);
13004         }
13005         
13006     },
13007     
13008     /**
13009      * Resets the current field value to the originally loaded value and clears any validation messages
13010      */
13011     reset : function(){
13012         this.setValue(this.originalValue);
13013         this.validate();
13014     },
13015      /**
13016      * Returns the name of the field
13017      * @return {Mixed} name The name field
13018      */
13019     getName: function(){
13020         return this.name;
13021     },
13022      /**
13023      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13024      * @return {Mixed} value The field value
13025      */
13026     getValue : function(){
13027         
13028         var v = this.inputEl().getValue();
13029         
13030         return v;
13031     },
13032     /**
13033      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13034      * @return {Mixed} value The field value
13035      */
13036     getRawValue : function(){
13037         var v = this.inputEl().getValue();
13038         
13039         return v;
13040     },
13041     
13042     /**
13043      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13044      * @param {Mixed} value The value to set
13045      */
13046     setRawValue : function(v){
13047         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13048     },
13049     
13050     selectText : function(start, end){
13051         var v = this.getRawValue();
13052         if(v.length > 0){
13053             start = start === undefined ? 0 : start;
13054             end = end === undefined ? v.length : end;
13055             var d = this.inputEl().dom;
13056             if(d.setSelectionRange){
13057                 d.setSelectionRange(start, end);
13058             }else if(d.createTextRange){
13059                 var range = d.createTextRange();
13060                 range.moveStart("character", start);
13061                 range.moveEnd("character", v.length-end);
13062                 range.select();
13063             }
13064         }
13065     },
13066     
13067     /**
13068      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13069      * @param {Mixed} value The value to set
13070      */
13071     setValue : function(v){
13072         this.value = v;
13073         if(this.rendered){
13074             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13075             this.validate();
13076         }
13077     },
13078     
13079     /*
13080     processValue : function(value){
13081         if(this.stripCharsRe){
13082             var newValue = value.replace(this.stripCharsRe, '');
13083             if(newValue !== value){
13084                 this.setRawValue(newValue);
13085                 return newValue;
13086             }
13087         }
13088         return value;
13089     },
13090   */
13091     preFocus : function(){
13092         
13093         if(this.selectOnFocus){
13094             this.inputEl().dom.select();
13095         }
13096     },
13097     filterKeys : function(e){
13098         var k = e.getKey();
13099         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13100             return;
13101         }
13102         var c = e.getCharCode(), cc = String.fromCharCode(c);
13103         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13104             return;
13105         }
13106         if(!this.maskRe.test(cc)){
13107             e.stopEvent();
13108         }
13109     },
13110      /**
13111      * Clear any invalid styles/messages for this field
13112      */
13113     clearInvalid : function(){
13114         
13115         if(!this.el || this.preventMark){ // not rendered
13116             return;
13117         }
13118         
13119         
13120         this.el.removeClass([this.invalidClass, 'is-invalid']);
13121         
13122         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13123             
13124             var feedback = this.el.select('.form-control-feedback', true).first();
13125             
13126             if(feedback){
13127                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13128             }
13129             
13130         }
13131         
13132         if(this.indicator){
13133             this.indicator.removeClass('visible');
13134             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13135         }
13136         
13137         this.fireEvent('valid', this);
13138     },
13139     
13140      /**
13141      * Mark this field as valid
13142      */
13143     markValid : function()
13144     {
13145         if(!this.el  || this.preventMark){ // not rendered...
13146             return;
13147         }
13148         
13149         this.el.removeClass([this.invalidClass, this.validClass]);
13150         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13151
13152         var feedback = this.el.select('.form-control-feedback', true).first();
13153             
13154         if(feedback){
13155             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13156         }
13157         
13158         if(this.indicator){
13159             this.indicator.removeClass('visible');
13160             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13161         }
13162         
13163         if(this.disabled){
13164             return;
13165         }
13166         
13167            
13168         if(this.allowBlank && !this.getRawValue().length){
13169             return;
13170         }
13171         if (Roo.bootstrap.version == 3) {
13172             this.el.addClass(this.validClass);
13173         } else {
13174             this.inputEl().addClass('is-valid');
13175         }
13176
13177         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13178             
13179             var feedback = this.el.select('.form-control-feedback', true).first();
13180             
13181             if(feedback){
13182                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13183                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13184             }
13185             
13186         }
13187         
13188         this.fireEvent('valid', this);
13189     },
13190     
13191      /**
13192      * Mark this field as invalid
13193      * @param {String} msg The validation message
13194      */
13195     markInvalid : function(msg)
13196     {
13197         if(!this.el  || this.preventMark){ // not rendered
13198             return;
13199         }
13200         
13201         this.el.removeClass([this.invalidClass, this.validClass]);
13202         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13203         
13204         var feedback = this.el.select('.form-control-feedback', true).first();
13205             
13206         if(feedback){
13207             this.el.select('.form-control-feedback', true).first().removeClass(
13208                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13209         }
13210
13211         if(this.disabled){
13212             return;
13213         }
13214         
13215         if(this.allowBlank && !this.getRawValue().length){
13216             return;
13217         }
13218         
13219         if(this.indicator){
13220             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13221             this.indicator.addClass('visible');
13222         }
13223         if (Roo.bootstrap.version == 3) {
13224             this.el.addClass(this.invalidClass);
13225         } else {
13226             this.inputEl().addClass('is-invalid');
13227         }
13228         
13229         
13230         
13231         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13232             
13233             var feedback = this.el.select('.form-control-feedback', true).first();
13234             
13235             if(feedback){
13236                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13237                 
13238                 if(this.getValue().length || this.forceFeedback){
13239                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13240                 }
13241                 
13242             }
13243             
13244         }
13245         
13246         this.fireEvent('invalid', this, msg);
13247     },
13248     // private
13249     SafariOnKeyDown : function(event)
13250     {
13251         // this is a workaround for a password hang bug on chrome/ webkit.
13252         if (this.inputEl().dom.type != 'password') {
13253             return;
13254         }
13255         
13256         var isSelectAll = false;
13257         
13258         if(this.inputEl().dom.selectionEnd > 0){
13259             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13260         }
13261         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13262             event.preventDefault();
13263             this.setValue('');
13264             return;
13265         }
13266         
13267         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13268             
13269             event.preventDefault();
13270             // this is very hacky as keydown always get's upper case.
13271             //
13272             var cc = String.fromCharCode(event.getCharCode());
13273             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13274             
13275         }
13276     },
13277     adjustWidth : function(tag, w){
13278         tag = tag.toLowerCase();
13279         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13280             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13281                 if(tag == 'input'){
13282                     return w + 2;
13283                 }
13284                 if(tag == 'textarea'){
13285                     return w-2;
13286                 }
13287             }else if(Roo.isOpera){
13288                 if(tag == 'input'){
13289                     return w + 2;
13290                 }
13291                 if(tag == 'textarea'){
13292                     return w-2;
13293                 }
13294             }
13295         }
13296         return w;
13297     },
13298     
13299     setFieldLabel : function(v)
13300     {
13301         if(!this.rendered){
13302             return;
13303         }
13304         
13305         if(this.indicatorEl()){
13306             var ar = this.el.select('label > span',true);
13307             
13308             if (ar.elements.length) {
13309                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13310                 this.fieldLabel = v;
13311                 return;
13312             }
13313             
13314             var br = this.el.select('label',true);
13315             
13316             if(br.elements.length) {
13317                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13318                 this.fieldLabel = v;
13319                 return;
13320             }
13321             
13322             Roo.log('Cannot Found any of label > span || label in input');
13323             return;
13324         }
13325         
13326         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13327         this.fieldLabel = v;
13328         
13329         
13330     }
13331 });
13332
13333  
13334 /*
13335  * - LGPL
13336  *
13337  * Input
13338  * 
13339  */
13340
13341 /**
13342  * @class Roo.bootstrap.form.TextArea
13343  * @extends Roo.bootstrap.form.Input
13344  * Bootstrap TextArea class
13345  * @cfg {Number} cols Specifies the visible width of a text area
13346  * @cfg {Number} rows Specifies the visible number of lines in a text area
13347  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13348  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13349  * @cfg {string} html text
13350  * 
13351  * @constructor
13352  * Create a new TextArea
13353  * @param {Object} config The config object
13354  */
13355
13356 Roo.bootstrap.form.TextArea = function(config){
13357     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13358    
13359 };
13360
13361 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13362      
13363     cols : false,
13364     rows : 5,
13365     readOnly : false,
13366     warp : 'soft',
13367     resize : false,
13368     value: false,
13369     html: false,
13370     
13371     getAutoCreate : function(){
13372         
13373         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13374         
13375         var id = Roo.id();
13376         
13377         var cfg = {};
13378         
13379         if(this.inputType != 'hidden'){
13380             cfg.cls = 'form-group' //input-group
13381         }
13382         
13383         var input =  {
13384             tag: 'textarea',
13385             id : id,
13386             warp : this.warp,
13387             rows : this.rows,
13388             value : this.value || '',
13389             html: this.html || '',
13390             cls : 'form-control',
13391             placeholder : this.placeholder || '' 
13392             
13393         };
13394         
13395         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13396             input.maxLength = this.maxLength;
13397         }
13398         
13399         if(this.resize){
13400             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13401         }
13402         
13403         if(this.cols){
13404             input.cols = this.cols;
13405         }
13406         
13407         if (this.readOnly) {
13408             input.readonly = true;
13409         }
13410         
13411         if (this.name) {
13412             input.name = this.name;
13413         }
13414         
13415         if (this.size) {
13416             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13417         }
13418         
13419         var settings=this;
13420         ['xs','sm','md','lg'].map(function(size){
13421             if (settings[size]) {
13422                 cfg.cls += ' col-' + size + '-' + settings[size];
13423             }
13424         });
13425         
13426         var inputblock = input;
13427         
13428         if(this.hasFeedback && !this.allowBlank){
13429             
13430             var feedback = {
13431                 tag: 'span',
13432                 cls: 'glyphicon form-control-feedback'
13433             };
13434
13435             inputblock = {
13436                 cls : 'has-feedback',
13437                 cn :  [
13438                     input,
13439                     feedback
13440                 ] 
13441             };  
13442         }
13443         
13444         
13445         if (this.before || this.after) {
13446             
13447             inputblock = {
13448                 cls : 'input-group',
13449                 cn :  [] 
13450             };
13451             if (this.before) {
13452                 inputblock.cn.push({
13453                     tag :'span',
13454                     cls : 'input-group-addon',
13455                     html : this.before
13456                 });
13457             }
13458             
13459             inputblock.cn.push(input);
13460             
13461             if(this.hasFeedback && !this.allowBlank){
13462                 inputblock.cls += ' has-feedback';
13463                 inputblock.cn.push(feedback);
13464             }
13465             
13466             if (this.after) {
13467                 inputblock.cn.push({
13468                     tag :'span',
13469                     cls : 'input-group-addon',
13470                     html : this.after
13471                 });
13472             }
13473             
13474         }
13475         
13476         if (align ==='left' && this.fieldLabel.length) {
13477             cfg.cn = [
13478                 {
13479                     tag: 'label',
13480                     'for' :  id,
13481                     cls : 'control-label',
13482                     html : this.fieldLabel
13483                 },
13484                 {
13485                     cls : "",
13486                     cn: [
13487                         inputblock
13488                     ]
13489                 }
13490
13491             ];
13492             
13493             if(this.labelWidth > 12){
13494                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13495             }
13496
13497             if(this.labelWidth < 13 && this.labelmd == 0){
13498                 this.labelmd = this.labelWidth;
13499             }
13500
13501             if(this.labellg > 0){
13502                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13503                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13504             }
13505
13506             if(this.labelmd > 0){
13507                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13508                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13509             }
13510
13511             if(this.labelsm > 0){
13512                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13513                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13514             }
13515
13516             if(this.labelxs > 0){
13517                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13518                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13519             }
13520             
13521         } else if ( this.fieldLabel.length) {
13522             cfg.cn = [
13523
13524                {
13525                    tag: 'label',
13526                    //cls : 'input-group-addon',
13527                    html : this.fieldLabel
13528
13529                },
13530
13531                inputblock
13532
13533            ];
13534
13535         } else {
13536
13537             cfg.cn = [
13538
13539                 inputblock
13540
13541             ];
13542                 
13543         }
13544         
13545         if (this.disabled) {
13546             input.disabled=true;
13547         }
13548         
13549         return cfg;
13550         
13551     },
13552     /**
13553      * return the real textarea element.
13554      */
13555     inputEl: function ()
13556     {
13557         return this.el.select('textarea.form-control',true).first();
13558     },
13559     
13560     /**
13561      * Clear any invalid styles/messages for this field
13562      */
13563     clearInvalid : function()
13564     {
13565         
13566         if(!this.el || this.preventMark){ // not rendered
13567             return;
13568         }
13569         
13570         var label = this.el.select('label', true).first();
13571         var icon = this.el.select('i.fa-star', true).first();
13572         
13573         if(label && icon){
13574             icon.remove();
13575         }
13576         this.el.removeClass( this.validClass);
13577         this.inputEl().removeClass('is-invalid');
13578          
13579         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13580             
13581             var feedback = this.el.select('.form-control-feedback', true).first();
13582             
13583             if(feedback){
13584                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13585             }
13586             
13587         }
13588         
13589         this.fireEvent('valid', this);
13590     },
13591     
13592      /**
13593      * Mark this field as valid
13594      */
13595     markValid : function()
13596     {
13597         if(!this.el  || this.preventMark){ // not rendered
13598             return;
13599         }
13600         
13601         this.el.removeClass([this.invalidClass, this.validClass]);
13602         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13603         
13604         var feedback = this.el.select('.form-control-feedback', true).first();
13605             
13606         if(feedback){
13607             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13608         }
13609
13610         if(this.disabled || this.allowBlank){
13611             return;
13612         }
13613         
13614         var label = this.el.select('label', true).first();
13615         var icon = this.el.select('i.fa-star', true).first();
13616         
13617         if(label && icon){
13618             icon.remove();
13619         }
13620         if (Roo.bootstrap.version == 3) {
13621             this.el.addClass(this.validClass);
13622         } else {
13623             this.inputEl().addClass('is-valid');
13624         }
13625         
13626         
13627         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13628             
13629             var feedback = this.el.select('.form-control-feedback', true).first();
13630             
13631             if(feedback){
13632                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13633                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13634             }
13635             
13636         }
13637         
13638         this.fireEvent('valid', this);
13639     },
13640     
13641      /**
13642      * Mark this field as invalid
13643      * @param {String} msg The validation message
13644      */
13645     markInvalid : function(msg)
13646     {
13647         if(!this.el  || this.preventMark){ // not rendered
13648             return;
13649         }
13650         
13651         this.el.removeClass([this.invalidClass, this.validClass]);
13652         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13653         
13654         var feedback = this.el.select('.form-control-feedback', true).first();
13655             
13656         if(feedback){
13657             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13658         }
13659
13660         if(this.disabled || this.allowBlank){
13661             return;
13662         }
13663         
13664         var label = this.el.select('label', true).first();
13665         var icon = this.el.select('i.fa-star', true).first();
13666         
13667         if(!this.getValue().length && label && !icon){
13668             this.el.createChild({
13669                 tag : 'i',
13670                 cls : 'text-danger fa fa-lg fa-star',
13671                 tooltip : 'This field is required',
13672                 style : 'margin-right:5px;'
13673             }, label, true);
13674         }
13675         
13676         if (Roo.bootstrap.version == 3) {
13677             this.el.addClass(this.invalidClass);
13678         } else {
13679             this.inputEl().addClass('is-invalid');
13680         }
13681         
13682         // fixme ... this may be depricated need to test..
13683         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13684             
13685             var feedback = this.el.select('.form-control-feedback', true).first();
13686             
13687             if(feedback){
13688                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13689                 
13690                 if(this.getValue().length || this.forceFeedback){
13691                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13692                 }
13693                 
13694             }
13695             
13696         }
13697         
13698         this.fireEvent('invalid', this, msg);
13699     }
13700 });
13701
13702  
13703 /*
13704  * - LGPL
13705  *
13706  * trigger field - base class for combo..
13707  * 
13708  */
13709  
13710 /**
13711  * @class Roo.bootstrap.form.TriggerField
13712  * @extends Roo.bootstrap.form.Input
13713  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13714  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13715  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13716  * for which you can provide a custom implementation.  For example:
13717  * <pre><code>
13718 var trigger = new Roo.bootstrap.form.TriggerField();
13719 trigger.onTriggerClick = myTriggerFn;
13720 trigger.applyTo('my-field');
13721 </code></pre>
13722  *
13723  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13724  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13725  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13726  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13727  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13728
13729  * @constructor
13730  * Create a new TriggerField.
13731  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13732  * to the base TextField)
13733  */
13734 Roo.bootstrap.form.TriggerField = function(config){
13735     this.mimicing = false;
13736     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13737 };
13738
13739 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13740     /**
13741      * @cfg {String} triggerClass A CSS class to apply to the trigger
13742      */
13743      /**
13744      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13745      */
13746     hideTrigger:false,
13747
13748     /**
13749      * @cfg {Boolean} removable (true|false) special filter default false
13750      */
13751     removable : false,
13752     
13753     /** @cfg {Boolean} grow @hide */
13754     /** @cfg {Number} growMin @hide */
13755     /** @cfg {Number} growMax @hide */
13756
13757     /**
13758      * @hide 
13759      * @method
13760      */
13761     autoSize: Roo.emptyFn,
13762     // private
13763     monitorTab : true,
13764     // private
13765     deferHeight : true,
13766
13767     
13768     actionMode : 'wrap',
13769     
13770     caret : false,
13771     
13772     
13773     getAutoCreate : function(){
13774        
13775         var align = this.labelAlign || this.parentLabelAlign();
13776         
13777         var id = Roo.id();
13778         
13779         var cfg = {
13780             cls: 'form-group' //input-group
13781         };
13782         
13783         
13784         var input =  {
13785             tag: 'input',
13786             id : id,
13787             type : this.inputType,
13788             cls : 'form-control',
13789             autocomplete: 'new-password',
13790             placeholder : this.placeholder || '' 
13791             
13792         };
13793         if (this.name) {
13794             input.name = this.name;
13795         }
13796         if (this.size) {
13797             input.cls += ' input-' + this.size;
13798         }
13799         
13800         if (this.disabled) {
13801             input.disabled=true;
13802         }
13803         
13804         var inputblock = input;
13805         
13806         if(this.hasFeedback && !this.allowBlank){
13807             
13808             var feedback = {
13809                 tag: 'span',
13810                 cls: 'glyphicon form-control-feedback'
13811             };
13812             
13813             if(this.removable && !this.editable  ){
13814                 inputblock = {
13815                     cls : 'has-feedback',
13816                     cn :  [
13817                         inputblock,
13818                         {
13819                             tag: 'button',
13820                             html : 'x',
13821                             cls : 'roo-combo-removable-btn close'
13822                         },
13823                         feedback
13824                     ] 
13825                 };
13826             } else {
13827                 inputblock = {
13828                     cls : 'has-feedback',
13829                     cn :  [
13830                         inputblock,
13831                         feedback
13832                     ] 
13833                 };
13834             }
13835
13836         } else {
13837             if(this.removable && !this.editable ){
13838                 inputblock = {
13839                     cls : 'roo-removable',
13840                     cn :  [
13841                         inputblock,
13842                         {
13843                             tag: 'button',
13844                             html : 'x',
13845                             cls : 'roo-combo-removable-btn close'
13846                         }
13847                     ] 
13848                 };
13849             }
13850         }
13851         
13852         if (this.before || this.after) {
13853             
13854             inputblock = {
13855                 cls : 'input-group',
13856                 cn :  [] 
13857             };
13858             if (this.before) {
13859                 inputblock.cn.push({
13860                     tag :'span',
13861                     cls : 'input-group-addon input-group-prepend input-group-text',
13862                     html : this.before
13863                 });
13864             }
13865             
13866             inputblock.cn.push(input);
13867             
13868             if(this.hasFeedback && !this.allowBlank){
13869                 inputblock.cls += ' has-feedback';
13870                 inputblock.cn.push(feedback);
13871             }
13872             
13873             if (this.after) {
13874                 inputblock.cn.push({
13875                     tag :'span',
13876                     cls : 'input-group-addon input-group-append input-group-text',
13877                     html : this.after
13878                 });
13879             }
13880             
13881         };
13882         
13883       
13884         
13885         var ibwrap = inputblock;
13886         
13887         if(this.multiple){
13888             ibwrap = {
13889                 tag: 'ul',
13890                 cls: 'roo-select2-choices',
13891                 cn:[
13892                     {
13893                         tag: 'li',
13894                         cls: 'roo-select2-search-field',
13895                         cn: [
13896
13897                             inputblock
13898                         ]
13899                     }
13900                 ]
13901             };
13902                 
13903         }
13904         
13905         var combobox = {
13906             cls: 'roo-select2-container input-group',
13907             cn: [
13908                  {
13909                     tag: 'input',
13910                     type : 'hidden',
13911                     cls: 'form-hidden-field'
13912                 },
13913                 ibwrap
13914             ]
13915         };
13916         
13917         if(!this.multiple && this.showToggleBtn){
13918             
13919             var caret = {
13920                         tag: 'span',
13921                         cls: 'caret'
13922              };
13923             if (this.caret != false) {
13924                 caret = {
13925                      tag: 'i',
13926                      cls: 'fa fa-' + this.caret
13927                 };
13928                 
13929             }
13930             
13931             combobox.cn.push({
13932                 tag :'span',
13933                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13934                 cn : [
13935                     Roo.bootstrap.version == 3 ? caret : '',
13936                     {
13937                         tag: 'span',
13938                         cls: 'combobox-clear',
13939                         cn  : [
13940                             {
13941                                 tag : 'i',
13942                                 cls: 'icon-remove'
13943                             }
13944                         ]
13945                     }
13946                 ]
13947
13948             })
13949         }
13950         
13951         if(this.multiple){
13952             combobox.cls += ' roo-select2-container-multi';
13953         }
13954          var indicator = {
13955             tag : 'i',
13956             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13957             tooltip : 'This field is required'
13958         };
13959         if (Roo.bootstrap.version == 4) {
13960             indicator = {
13961                 tag : 'i',
13962                 style : 'display:none'
13963             };
13964         }
13965         
13966         
13967         if (align ==='left' && this.fieldLabel.length) {
13968             
13969             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13970
13971             cfg.cn = [
13972                 indicator,
13973                 {
13974                     tag: 'label',
13975                     'for' :  id,
13976                     cls : 'control-label',
13977                     html : this.fieldLabel
13978
13979                 },
13980                 {
13981                     cls : "", 
13982                     cn: [
13983                         combobox
13984                     ]
13985                 }
13986
13987             ];
13988             
13989             var labelCfg = cfg.cn[1];
13990             var contentCfg = cfg.cn[2];
13991             
13992             if(this.indicatorpos == 'right'){
13993                 cfg.cn = [
13994                     {
13995                         tag: 'label',
13996                         'for' :  id,
13997                         cls : 'control-label',
13998                         cn : [
13999                             {
14000                                 tag : 'span',
14001                                 html : this.fieldLabel
14002                             },
14003                             indicator
14004                         ]
14005                     },
14006                     {
14007                         cls : "", 
14008                         cn: [
14009                             combobox
14010                         ]
14011                     }
14012
14013                 ];
14014                 
14015                 labelCfg = cfg.cn[0];
14016                 contentCfg = cfg.cn[1];
14017             }
14018             
14019             if(this.labelWidth > 12){
14020                 labelCfg.style = "width: " + this.labelWidth + 'px';
14021             }
14022             
14023             if(this.labelWidth < 13 && this.labelmd == 0){
14024                 this.labelmd = this.labelWidth;
14025             }
14026             
14027             if(this.labellg > 0){
14028                 labelCfg.cls += ' col-lg-' + this.labellg;
14029                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14030             }
14031             
14032             if(this.labelmd > 0){
14033                 labelCfg.cls += ' col-md-' + this.labelmd;
14034                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14035             }
14036             
14037             if(this.labelsm > 0){
14038                 labelCfg.cls += ' col-sm-' + this.labelsm;
14039                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14040             }
14041             
14042             if(this.labelxs > 0){
14043                 labelCfg.cls += ' col-xs-' + this.labelxs;
14044                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14045             }
14046             
14047         } else if ( this.fieldLabel.length) {
14048 //                Roo.log(" label");
14049             cfg.cn = [
14050                 indicator,
14051                {
14052                    tag: 'label',
14053                    //cls : 'input-group-addon',
14054                    html : this.fieldLabel
14055
14056                },
14057
14058                combobox
14059
14060             ];
14061             
14062             if(this.indicatorpos == 'right'){
14063                 
14064                 cfg.cn = [
14065                     {
14066                        tag: 'label',
14067                        cn : [
14068                            {
14069                                tag : 'span',
14070                                html : this.fieldLabel
14071                            },
14072                            indicator
14073                        ]
14074
14075                     },
14076                     combobox
14077
14078                 ];
14079
14080             }
14081
14082         } else {
14083             
14084 //                Roo.log(" no label && no align");
14085                 cfg = combobox
14086                      
14087                 
14088         }
14089         
14090         var settings=this;
14091         ['xs','sm','md','lg'].map(function(size){
14092             if (settings[size]) {
14093                 cfg.cls += ' col-' + size + '-' + settings[size];
14094             }
14095         });
14096         
14097         return cfg;
14098         
14099     },
14100     
14101     
14102     
14103     // private
14104     onResize : function(w, h){
14105 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14106 //        if(typeof w == 'number'){
14107 //            var x = w - this.trigger.getWidth();
14108 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14109 //            this.trigger.setStyle('left', x+'px');
14110 //        }
14111     },
14112
14113     // private
14114     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14115
14116     // private
14117     getResizeEl : function(){
14118         return this.inputEl();
14119     },
14120
14121     // private
14122     getPositionEl : function(){
14123         return this.inputEl();
14124     },
14125
14126     // private
14127     alignErrorIcon : function(){
14128         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14129     },
14130
14131     // private
14132     initEvents : function(){
14133         
14134         this.createList();
14135         
14136         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14137         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14138         if(!this.multiple && this.showToggleBtn){
14139             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14140             if(this.hideTrigger){
14141                 this.trigger.setDisplayed(false);
14142             }
14143             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14144         }
14145         
14146         if(this.multiple){
14147             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14148         }
14149         
14150         if(this.removable && !this.editable && !this.tickable){
14151             var close = this.closeTriggerEl();
14152             
14153             if(close){
14154                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14155                 close.on('click', this.removeBtnClick, this, close);
14156             }
14157         }
14158         
14159         //this.trigger.addClassOnOver('x-form-trigger-over');
14160         //this.trigger.addClassOnClick('x-form-trigger-click');
14161         
14162         //if(!this.width){
14163         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14164         //}
14165     },
14166     
14167     closeTriggerEl : function()
14168     {
14169         var close = this.el.select('.roo-combo-removable-btn', true).first();
14170         return close ? close : false;
14171     },
14172     
14173     removeBtnClick : function(e, h, el)
14174     {
14175         e.preventDefault();
14176         
14177         if(this.fireEvent("remove", this) !== false){
14178             this.reset();
14179             this.fireEvent("afterremove", this)
14180         }
14181     },
14182     
14183     createList : function()
14184     {
14185         this.list = Roo.get(document.body).createChild({
14186             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14187             cls: 'typeahead typeahead-long dropdown-menu shadow',
14188             style: 'display:none'
14189         });
14190         
14191         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14192         
14193     },
14194
14195     // private
14196     initTrigger : function(){
14197        
14198     },
14199
14200     // private
14201     onDestroy : function(){
14202         if(this.trigger){
14203             this.trigger.removeAllListeners();
14204           //  this.trigger.remove();
14205         }
14206         //if(this.wrap){
14207         //    this.wrap.remove();
14208         //}
14209         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14210     },
14211
14212     // private
14213     onFocus : function(){
14214         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14215         /*
14216         if(!this.mimicing){
14217             this.wrap.addClass('x-trigger-wrap-focus');
14218             this.mimicing = true;
14219             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14220             if(this.monitorTab){
14221                 this.el.on("keydown", this.checkTab, this);
14222             }
14223         }
14224         */
14225     },
14226
14227     // private
14228     checkTab : function(e){
14229         if(e.getKey() == e.TAB){
14230             this.triggerBlur();
14231         }
14232     },
14233
14234     // private
14235     onBlur : function(){
14236         // do nothing
14237     },
14238
14239     // private
14240     mimicBlur : function(e, t){
14241         /*
14242         if(!this.wrap.contains(t) && this.validateBlur()){
14243             this.triggerBlur();
14244         }
14245         */
14246     },
14247
14248     // private
14249     triggerBlur : function(){
14250         this.mimicing = false;
14251         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14252         if(this.monitorTab){
14253             this.el.un("keydown", this.checkTab, this);
14254         }
14255         //this.wrap.removeClass('x-trigger-wrap-focus');
14256         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14257     },
14258
14259     // private
14260     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14261     validateBlur : function(e, t){
14262         return true;
14263     },
14264
14265     // private
14266     onDisable : function(){
14267         this.inputEl().dom.disabled = true;
14268         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14269         //if(this.wrap){
14270         //    this.wrap.addClass('x-item-disabled');
14271         //}
14272     },
14273
14274     // private
14275     onEnable : function(){
14276         this.inputEl().dom.disabled = false;
14277         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14278         //if(this.wrap){
14279         //    this.el.removeClass('x-item-disabled');
14280         //}
14281     },
14282
14283     // private
14284     onShow : function(){
14285         var ae = this.getActionEl();
14286         
14287         if(ae){
14288             ae.dom.style.display = '';
14289             ae.dom.style.visibility = 'visible';
14290         }
14291     },
14292
14293     // private
14294     
14295     onHide : function(){
14296         var ae = this.getActionEl();
14297         ae.dom.style.display = 'none';
14298     },
14299
14300     /**
14301      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14302      * by an implementing function.
14303      * @method
14304      * @param {EventObject} e
14305      */
14306     onTriggerClick : Roo.emptyFn
14307 });
14308  
14309 /*
14310 * Licence: LGPL
14311 */
14312
14313 /**
14314  * @class Roo.bootstrap.form.CardUploader
14315  * @extends Roo.bootstrap.Button
14316  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14317  * @cfg {Number} errorTimeout default 3000
14318  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14319  * @cfg {Array}  html The button text.
14320
14321  *
14322  * @constructor
14323  * Create a new CardUploader
14324  * @param {Object} config The config object
14325  */
14326
14327 Roo.bootstrap.form.CardUploader = function(config){
14328     
14329  
14330     
14331     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14332     
14333     
14334     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14335         return r.data.id
14336      });
14337     
14338      this.addEvents({
14339          // raw events
14340         /**
14341          * @event preview
14342          * When a image is clicked on - and needs to display a slideshow or similar..
14343          * @param {Roo.bootstrap.Card} this
14344          * @param {Object} The image information data 
14345          *
14346          */
14347         'preview' : true,
14348          /**
14349          * @event download
14350          * When a the download link is clicked
14351          * @param {Roo.bootstrap.Card} this
14352          * @param {Object} The image information data  contains 
14353          */
14354         'download' : true
14355         
14356     });
14357 };
14358  
14359 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14360     
14361      
14362     errorTimeout : 3000,
14363      
14364     images : false,
14365    
14366     fileCollection : false,
14367     allowBlank : true,
14368     
14369     getAutoCreate : function()
14370     {
14371         
14372         var cfg =  {
14373             cls :'form-group' ,
14374             cn : [
14375                
14376                 {
14377                     tag: 'label',
14378                    //cls : 'input-group-addon',
14379                     html : this.fieldLabel
14380
14381                 },
14382
14383                 {
14384                     tag: 'input',
14385                     type : 'hidden',
14386                     name : this.name,
14387                     value : this.value,
14388                     cls : 'd-none  form-control'
14389                 },
14390                 
14391                 {
14392                     tag: 'input',
14393                     multiple : 'multiple',
14394                     type : 'file',
14395                     cls : 'd-none  roo-card-upload-selector'
14396                 },
14397                 
14398                 {
14399                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14400                 },
14401                 {
14402                     cls : 'card-columns roo-card-uploader-container'
14403                 }
14404
14405             ]
14406         };
14407            
14408          
14409         return cfg;
14410     },
14411     
14412     getChildContainer : function() /// what children are added to.
14413     {
14414         return this.containerEl;
14415     },
14416    
14417     getButtonContainer : function() /// what children are added to.
14418     {
14419         return this.el.select(".roo-card-uploader-button-container").first();
14420     },
14421    
14422     initEvents : function()
14423     {
14424         
14425         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14426         
14427         var t = this;
14428         this.addxtype({
14429             xns: Roo.bootstrap,
14430
14431             xtype : 'Button',
14432             container_method : 'getButtonContainer' ,            
14433             html :  this.html, // fix changable?
14434             cls : 'w-100 ',
14435             listeners : {
14436                 'click' : function(btn, e) {
14437                     t.onClick(e);
14438                 }
14439             }
14440         });
14441         
14442         
14443         
14444         
14445         this.urlAPI = (window.createObjectURL && window) || 
14446                                 (window.URL && URL.revokeObjectURL && URL) || 
14447                                 (window.webkitURL && webkitURL);
14448                         
14449          
14450          
14451          
14452         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14453         
14454         this.selectorEl.on('change', this.onFileSelected, this);
14455         if (this.images) {
14456             var t = this;
14457             this.images.forEach(function(img) {
14458                 t.addCard(img)
14459             });
14460             this.images = false;
14461         }
14462         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14463          
14464        
14465     },
14466     
14467    
14468     onClick : function(e)
14469     {
14470         e.preventDefault();
14471          
14472         this.selectorEl.dom.click();
14473          
14474     },
14475     
14476     onFileSelected : function(e)
14477     {
14478         e.preventDefault();
14479         
14480         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14481             return;
14482         }
14483         
14484         Roo.each(this.selectorEl.dom.files, function(file){    
14485             this.addFile(file);
14486         }, this);
14487          
14488     },
14489     
14490       
14491     
14492       
14493     
14494     addFile : function(file)
14495     {
14496            
14497         if(typeof(file) === 'string'){
14498             throw "Add file by name?"; // should not happen
14499             return;
14500         }
14501         
14502         if(!file || !this.urlAPI){
14503             return;
14504         }
14505         
14506         // file;
14507         // file.type;
14508         
14509         var _this = this;
14510         
14511         
14512         var url = _this.urlAPI.createObjectURL( file);
14513            
14514         this.addCard({
14515             id : Roo.bootstrap.form.CardUploader.ID--,
14516             is_uploaded : false,
14517             src : url,
14518             srcfile : file,
14519             title : file.name,
14520             mimetype : file.type,
14521             preview : false,
14522             is_deleted : 0
14523         });
14524         
14525     },
14526     
14527     /**
14528      * addCard - add an Attachment to the uploader
14529      * @param data - the data about the image to upload
14530      *
14531      * {
14532           id : 123
14533           title : "Title of file",
14534           is_uploaded : false,
14535           src : "http://.....",
14536           srcfile : { the File upload object },
14537           mimetype : file.type,
14538           preview : false,
14539           is_deleted : 0
14540           .. any other data...
14541         }
14542      *
14543      * 
14544     */
14545     
14546     addCard : function (data)
14547     {
14548         // hidden input element?
14549         // if the file is not an image...
14550         //then we need to use something other that and header_image
14551         var t = this;
14552         //   remove.....
14553         var footer = [
14554             {
14555                 xns : Roo.bootstrap,
14556                 xtype : 'CardFooter',
14557                  items: [
14558                     {
14559                         xns : Roo.bootstrap,
14560                         xtype : 'Element',
14561                         cls : 'd-flex',
14562                         items : [
14563                             
14564                             {
14565                                 xns : Roo.bootstrap,
14566                                 xtype : 'Button',
14567                                 html : String.format("<small>{0}</small>", data.title),
14568                                 cls : 'col-10 text-left',
14569                                 size: 'sm',
14570                                 weight: 'link',
14571                                 fa : 'download',
14572                                 listeners : {
14573                                     click : function() {
14574                                      
14575                                         t.fireEvent( "download", t, data );
14576                                     }
14577                                 }
14578                             },
14579                           
14580                             {
14581                                 xns : Roo.bootstrap,
14582                                 xtype : 'Button',
14583                                 style: 'max-height: 28px; ',
14584                                 size : 'sm',
14585                                 weight: 'danger',
14586                                 cls : 'col-2',
14587                                 fa : 'times',
14588                                 listeners : {
14589                                     click : function() {
14590                                         t.removeCard(data.id)
14591                                     }
14592                                 }
14593                             }
14594                         ]
14595                     }
14596                     
14597                 ] 
14598             }
14599             
14600         ];
14601         
14602         var cn = this.addxtype(
14603             {
14604                  
14605                 xns : Roo.bootstrap,
14606                 xtype : 'Card',
14607                 closeable : true,
14608                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14609                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14610                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14611                 data : data,
14612                 html : false,
14613                  
14614                 items : footer,
14615                 initEvents : function() {
14616                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14617                     var card = this;
14618                     this.imgEl = this.el.select('.card-img-top').first();
14619                     if (this.imgEl) {
14620                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14621                         this.imgEl.set({ 'pointer' : 'cursor' });
14622                                   
14623                     }
14624                     this.getCardFooter().addClass('p-1');
14625                     
14626                   
14627                 }
14628                 
14629             }
14630         );
14631         // dont' really need ot update items.
14632         // this.items.push(cn);
14633         this.fileCollection.add(cn);
14634         
14635         if (!data.srcfile) {
14636             this.updateInput();
14637             return;
14638         }
14639             
14640         var _t = this;
14641         var reader = new FileReader();
14642         reader.addEventListener("load", function() {  
14643             data.srcdata =  reader.result;
14644             _t.updateInput();
14645         });
14646         reader.readAsDataURL(data.srcfile);
14647         
14648         
14649         
14650     },
14651     removeCard : function(id)
14652     {
14653         
14654         var card  = this.fileCollection.get(id);
14655         card.data.is_deleted = 1;
14656         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14657         //this.fileCollection.remove(card);
14658         //this.items = this.items.filter(function(e) { return e != card });
14659         // dont' really need ot update items.
14660         card.el.dom.parentNode.removeChild(card.el.dom);
14661         this.updateInput();
14662
14663         
14664     },
14665     reset: function()
14666     {
14667         this.fileCollection.each(function(card) {
14668             if (card.el.dom && card.el.dom.parentNode) {
14669                 card.el.dom.parentNode.removeChild(card.el.dom);
14670             }
14671         });
14672         this.fileCollection.clear();
14673         this.updateInput();
14674     },
14675     
14676     updateInput : function()
14677     {
14678          var data = [];
14679         this.fileCollection.each(function(e) {
14680             data.push(e.data);
14681             
14682         });
14683         this.inputEl().dom.value = JSON.stringify(data);
14684         
14685         
14686         
14687     }
14688     
14689     
14690 });
14691
14692
14693 Roo.bootstrap.form.CardUploader.ID = -1;/*
14694  * Based on:
14695  * Ext JS Library 1.1.1
14696  * Copyright(c) 2006-2007, Ext JS, LLC.
14697  *
14698  * Originally Released Under LGPL - original licence link has changed is not relivant.
14699  *
14700  * Fork - LGPL
14701  * <script type="text/javascript">
14702  */
14703
14704
14705 /**
14706  * @class Roo.data.SortTypes
14707  * @static
14708  * Defines the default sorting (casting?) comparison functions used when sorting data.
14709  */
14710 Roo.data.SortTypes = {
14711     /**
14712      * Default sort that does nothing
14713      * @param {Mixed} s The value being converted
14714      * @return {Mixed} The comparison value
14715      */
14716     none : function(s){
14717         return s;
14718     },
14719     
14720     /**
14721      * The regular expression used to strip tags
14722      * @type {RegExp}
14723      * @property
14724      */
14725     stripTagsRE : /<\/?[^>]+>/gi,
14726     
14727     /**
14728      * Strips all HTML tags to sort on text only
14729      * @param {Mixed} s The value being converted
14730      * @return {String} The comparison value
14731      */
14732     asText : function(s){
14733         return String(s).replace(this.stripTagsRE, "");
14734     },
14735     
14736     /**
14737      * Strips all HTML tags to sort on text only - Case insensitive
14738      * @param {Mixed} s The value being converted
14739      * @return {String} The comparison value
14740      */
14741     asUCText : function(s){
14742         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14743     },
14744     
14745     /**
14746      * Case insensitive string
14747      * @param {Mixed} s The value being converted
14748      * @return {String} The comparison value
14749      */
14750     asUCString : function(s) {
14751         return String(s).toUpperCase();
14752     },
14753     
14754     /**
14755      * Date sorting
14756      * @param {Mixed} s The value being converted
14757      * @return {Number} The comparison value
14758      */
14759     asDate : function(s) {
14760         if(!s){
14761             return 0;
14762         }
14763         if(s instanceof Date){
14764             return s.getTime();
14765         }
14766         return Date.parse(String(s));
14767     },
14768     
14769     /**
14770      * Float sorting
14771      * @param {Mixed} s The value being converted
14772      * @return {Float} The comparison value
14773      */
14774     asFloat : function(s) {
14775         var val = parseFloat(String(s).replace(/,/g, ""));
14776         if(isNaN(val)) {
14777             val = 0;
14778         }
14779         return val;
14780     },
14781     
14782     /**
14783      * Integer sorting
14784      * @param {Mixed} s The value being converted
14785      * @return {Number} The comparison value
14786      */
14787     asInt : function(s) {
14788         var val = parseInt(String(s).replace(/,/g, ""));
14789         if(isNaN(val)) {
14790             val = 0;
14791         }
14792         return val;
14793     }
14794 };/*
14795  * Based on:
14796  * Ext JS Library 1.1.1
14797  * Copyright(c) 2006-2007, Ext JS, LLC.
14798  *
14799  * Originally Released Under LGPL - original licence link has changed is not relivant.
14800  *
14801  * Fork - LGPL
14802  * <script type="text/javascript">
14803  */
14804
14805 /**
14806 * @class Roo.data.Record
14807  * Instances of this class encapsulate both record <em>definition</em> information, and record
14808  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14809  * to access Records cached in an {@link Roo.data.Store} object.<br>
14810  * <p>
14811  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14812  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14813  * objects.<br>
14814  * <p>
14815  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14816  * @constructor
14817  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14818  * {@link #create}. The parameters are the same.
14819  * @param {Array} data An associative Array of data values keyed by the field name.
14820  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14821  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14822  * not specified an integer id is generated.
14823  */
14824 Roo.data.Record = function(data, id){
14825     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14826     this.data = data;
14827 };
14828
14829 /**
14830  * Generate a constructor for a specific record layout.
14831  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14832  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14833  * Each field definition object may contain the following properties: <ul>
14834  * <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,
14835  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14836  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14837  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14838  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14839  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14840  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14841  * this may be omitted.</p></li>
14842  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14843  * <ul><li>auto (Default, implies no conversion)</li>
14844  * <li>string</li>
14845  * <li>int</li>
14846  * <li>float</li>
14847  * <li>boolean</li>
14848  * <li>date</li></ul></p></li>
14849  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14850  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14851  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14852  * by the Reader into an object that will be stored in the Record. It is passed the
14853  * following parameters:<ul>
14854  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14855  * </ul></p></li>
14856  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14857  * </ul>
14858  * <br>usage:<br><pre><code>
14859 var TopicRecord = Roo.data.Record.create(
14860     {name: 'title', mapping: 'topic_title'},
14861     {name: 'author', mapping: 'username'},
14862     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14863     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14864     {name: 'lastPoster', mapping: 'user2'},
14865     {name: 'excerpt', mapping: 'post_text'}
14866 );
14867
14868 var myNewRecord = new TopicRecord({
14869     title: 'Do my job please',
14870     author: 'noobie',
14871     totalPosts: 1,
14872     lastPost: new Date(),
14873     lastPoster: 'Animal',
14874     excerpt: 'No way dude!'
14875 });
14876 myStore.add(myNewRecord);
14877 </code></pre>
14878  * @method create
14879  * @static
14880  */
14881 Roo.data.Record.create = function(o){
14882     var f = function(){
14883         f.superclass.constructor.apply(this, arguments);
14884     };
14885     Roo.extend(f, Roo.data.Record);
14886     var p = f.prototype;
14887     p.fields = new Roo.util.MixedCollection(false, function(field){
14888         return field.name;
14889     });
14890     for(var i = 0, len = o.length; i < len; i++){
14891         p.fields.add(new Roo.data.Field(o[i]));
14892     }
14893     f.getField = function(name){
14894         return p.fields.get(name);  
14895     };
14896     return f;
14897 };
14898
14899 Roo.data.Record.AUTO_ID = 1000;
14900 Roo.data.Record.EDIT = 'edit';
14901 Roo.data.Record.REJECT = 'reject';
14902 Roo.data.Record.COMMIT = 'commit';
14903
14904 Roo.data.Record.prototype = {
14905     /**
14906      * Readonly flag - true if this record has been modified.
14907      * @type Boolean
14908      */
14909     dirty : false,
14910     editing : false,
14911     error: null,
14912     modified: null,
14913
14914     // private
14915     join : function(store){
14916         this.store = store;
14917     },
14918
14919     /**
14920      * Set the named field to the specified value.
14921      * @param {String} name The name of the field to set.
14922      * @param {Object} value The value to set the field to.
14923      */
14924     set : function(name, value){
14925         if(this.data[name] == value){
14926             return;
14927         }
14928         this.dirty = true;
14929         if(!this.modified){
14930             this.modified = {};
14931         }
14932         if(typeof this.modified[name] == 'undefined'){
14933             this.modified[name] = this.data[name];
14934         }
14935         this.data[name] = value;
14936         if(!this.editing && this.store){
14937             this.store.afterEdit(this);
14938         }       
14939     },
14940
14941     /**
14942      * Get the value of the named field.
14943      * @param {String} name The name of the field to get the value of.
14944      * @return {Object} The value of the field.
14945      */
14946     get : function(name){
14947         return this.data[name]; 
14948     },
14949
14950     // private
14951     beginEdit : function(){
14952         this.editing = true;
14953         this.modified = {}; 
14954     },
14955
14956     // private
14957     cancelEdit : function(){
14958         this.editing = false;
14959         delete this.modified;
14960     },
14961
14962     // private
14963     endEdit : function(){
14964         this.editing = false;
14965         if(this.dirty && this.store){
14966             this.store.afterEdit(this);
14967         }
14968     },
14969
14970     /**
14971      * Usually called by the {@link Roo.data.Store} which owns the Record.
14972      * Rejects all changes made to the Record since either creation, or the last commit operation.
14973      * Modified fields are reverted to their original values.
14974      * <p>
14975      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14976      * of reject operations.
14977      */
14978     reject : function(){
14979         var m = this.modified;
14980         for(var n in m){
14981             if(typeof m[n] != "function"){
14982                 this.data[n] = m[n];
14983             }
14984         }
14985         this.dirty = false;
14986         delete this.modified;
14987         this.editing = false;
14988         if(this.store){
14989             this.store.afterReject(this);
14990         }
14991     },
14992
14993     /**
14994      * Usually called by the {@link Roo.data.Store} which owns the Record.
14995      * Commits all changes made to the Record since either creation, or the last commit operation.
14996      * <p>
14997      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14998      * of commit operations.
14999      */
15000     commit : function(){
15001         this.dirty = false;
15002         delete this.modified;
15003         this.editing = false;
15004         if(this.store){
15005             this.store.afterCommit(this);
15006         }
15007     },
15008
15009     // private
15010     hasError : function(){
15011         return this.error != null;
15012     },
15013
15014     // private
15015     clearError : function(){
15016         this.error = null;
15017     },
15018
15019     /**
15020      * Creates a copy of this record.
15021      * @param {String} id (optional) A new record id if you don't want to use this record's id
15022      * @return {Record}
15023      */
15024     copy : function(newId) {
15025         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15026     }
15027 };/*
15028  * Based on:
15029  * Ext JS Library 1.1.1
15030  * Copyright(c) 2006-2007, Ext JS, LLC.
15031  *
15032  * Originally Released Under LGPL - original licence link has changed is not relivant.
15033  *
15034  * Fork - LGPL
15035  * <script type="text/javascript">
15036  */
15037
15038
15039
15040 /**
15041  * @class Roo.data.Store
15042  * @extends Roo.util.Observable
15043  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15044  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15045  * <p>
15046  * 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
15047  * has no knowledge of the format of the data returned by the Proxy.<br>
15048  * <p>
15049  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15050  * instances from the data object. These records are cached and made available through accessor functions.
15051  * @constructor
15052  * Creates a new Store.
15053  * @param {Object} config A config object containing the objects needed for the Store to access data,
15054  * and read the data into Records.
15055  */
15056 Roo.data.Store = function(config){
15057     this.data = new Roo.util.MixedCollection(false);
15058     this.data.getKey = function(o){
15059         return o.id;
15060     };
15061     this.baseParams = {};
15062     // private
15063     this.paramNames = {
15064         "start" : "start",
15065         "limit" : "limit",
15066         "sort" : "sort",
15067         "dir" : "dir",
15068         "multisort" : "_multisort"
15069     };
15070
15071     if(config && config.data){
15072         this.inlineData = config.data;
15073         delete config.data;
15074     }
15075
15076     Roo.apply(this, config);
15077     
15078     if(this.reader){ // reader passed
15079         this.reader = Roo.factory(this.reader, Roo.data);
15080         this.reader.xmodule = this.xmodule || false;
15081         if(!this.recordType){
15082             this.recordType = this.reader.recordType;
15083         }
15084         if(this.reader.onMetaChange){
15085             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15086         }
15087     }
15088
15089     if(this.recordType){
15090         this.fields = this.recordType.prototype.fields;
15091     }
15092     this.modified = [];
15093
15094     this.addEvents({
15095         /**
15096          * @event datachanged
15097          * Fires when the data cache has changed, and a widget which is using this Store
15098          * as a Record cache should refresh its view.
15099          * @param {Store} this
15100          */
15101         datachanged : true,
15102         /**
15103          * @event metachange
15104          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15105          * @param {Store} this
15106          * @param {Object} meta The JSON metadata
15107          */
15108         metachange : true,
15109         /**
15110          * @event add
15111          * Fires when Records have been added to the Store
15112          * @param {Store} this
15113          * @param {Roo.data.Record[]} records The array of Records added
15114          * @param {Number} index The index at which the record(s) were added
15115          */
15116         add : true,
15117         /**
15118          * @event remove
15119          * Fires when a Record has been removed from the Store
15120          * @param {Store} this
15121          * @param {Roo.data.Record} record The Record that was removed
15122          * @param {Number} index The index at which the record was removed
15123          */
15124         remove : true,
15125         /**
15126          * @event update
15127          * Fires when a Record has been updated
15128          * @param {Store} this
15129          * @param {Roo.data.Record} record The Record that was updated
15130          * @param {String} operation The update operation being performed.  Value may be one of:
15131          * <pre><code>
15132  Roo.data.Record.EDIT
15133  Roo.data.Record.REJECT
15134  Roo.data.Record.COMMIT
15135          * </code></pre>
15136          */
15137         update : true,
15138         /**
15139          * @event clear
15140          * Fires when the data cache has been cleared.
15141          * @param {Store} this
15142          */
15143         clear : true,
15144         /**
15145          * @event beforeload
15146          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15147          * the load action will be canceled.
15148          * @param {Store} this
15149          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15150          */
15151         beforeload : true,
15152         /**
15153          * @event beforeloadadd
15154          * Fires after a new set of Records has been loaded.
15155          * @param {Store} this
15156          * @param {Roo.data.Record[]} records The Records that were loaded
15157          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15158          */
15159         beforeloadadd : true,
15160         /**
15161          * @event load
15162          * Fires after a new set of Records has been loaded, before they are added to the store.
15163          * @param {Store} this
15164          * @param {Roo.data.Record[]} records The Records that were loaded
15165          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15166          * @params {Object} return from reader
15167          */
15168         load : true,
15169         /**
15170          * @event loadexception
15171          * Fires if an exception occurs in the Proxy during loading.
15172          * Called with the signature of the Proxy's "loadexception" event.
15173          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15174          * 
15175          * @param {Proxy} 
15176          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15177          * @param {Object} load options 
15178          * @param {Object} jsonData from your request (normally this contains the Exception)
15179          */
15180         loadexception : true
15181     });
15182     
15183     if(this.proxy){
15184         this.proxy = Roo.factory(this.proxy, Roo.data);
15185         this.proxy.xmodule = this.xmodule || false;
15186         this.relayEvents(this.proxy,  ["loadexception"]);
15187     }
15188     this.sortToggle = {};
15189     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15190
15191     Roo.data.Store.superclass.constructor.call(this);
15192
15193     if(this.inlineData){
15194         this.loadData(this.inlineData);
15195         delete this.inlineData;
15196     }
15197 };
15198
15199 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15200      /**
15201     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15202     * without a remote query - used by combo/forms at present.
15203     */
15204     
15205     /**
15206     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15207     */
15208     /**
15209     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15210     */
15211     /**
15212     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15213     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15214     */
15215     /**
15216     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15217     * on any HTTP request
15218     */
15219     /**
15220     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15221     */
15222     /**
15223     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15224     */
15225     multiSort: false,
15226     /**
15227     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15228     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15229     */
15230     remoteSort : false,
15231
15232     /**
15233     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15234      * loaded or when a record is removed. (defaults to false).
15235     */
15236     pruneModifiedRecords : false,
15237
15238     // private
15239     lastOptions : null,
15240
15241     /**
15242      * Add Records to the Store and fires the add event.
15243      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15244      */
15245     add : function(records){
15246         records = [].concat(records);
15247         for(var i = 0, len = records.length; i < len; i++){
15248             records[i].join(this);
15249         }
15250         var index = this.data.length;
15251         this.data.addAll(records);
15252         this.fireEvent("add", this, records, index);
15253     },
15254
15255     /**
15256      * Remove a Record from the Store and fires the remove event.
15257      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15258      */
15259     remove : function(record){
15260         var index = this.data.indexOf(record);
15261         this.data.removeAt(index);
15262  
15263         if(this.pruneModifiedRecords){
15264             this.modified.remove(record);
15265         }
15266         this.fireEvent("remove", this, record, index);
15267     },
15268
15269     /**
15270      * Remove all Records from the Store and fires the clear event.
15271      */
15272     removeAll : function(){
15273         this.data.clear();
15274         if(this.pruneModifiedRecords){
15275             this.modified = [];
15276         }
15277         this.fireEvent("clear", this);
15278     },
15279
15280     /**
15281      * Inserts Records to the Store at the given index and fires the add event.
15282      * @param {Number} index The start index at which to insert the passed Records.
15283      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15284      */
15285     insert : function(index, records){
15286         records = [].concat(records);
15287         for(var i = 0, len = records.length; i < len; i++){
15288             this.data.insert(index, records[i]);
15289             records[i].join(this);
15290         }
15291         this.fireEvent("add", this, records, index);
15292     },
15293
15294     /**
15295      * Get the index within the cache of the passed Record.
15296      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15297      * @return {Number} The index of the passed Record. Returns -1 if not found.
15298      */
15299     indexOf : function(record){
15300         return this.data.indexOf(record);
15301     },
15302
15303     /**
15304      * Get the index within the cache of the Record with the passed id.
15305      * @param {String} id The id of the Record to find.
15306      * @return {Number} The index of the Record. Returns -1 if not found.
15307      */
15308     indexOfId : function(id){
15309         return this.data.indexOfKey(id);
15310     },
15311
15312     /**
15313      * Get the Record with the specified id.
15314      * @param {String} id The id of the Record to find.
15315      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15316      */
15317     getById : function(id){
15318         return this.data.key(id);
15319     },
15320
15321     /**
15322      * Get the Record at the specified index.
15323      * @param {Number} index The index of the Record to find.
15324      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15325      */
15326     getAt : function(index){
15327         return this.data.itemAt(index);
15328     },
15329
15330     /**
15331      * Returns a range of Records between specified indices.
15332      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15333      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15334      * @return {Roo.data.Record[]} An array of Records
15335      */
15336     getRange : function(start, end){
15337         return this.data.getRange(start, end);
15338     },
15339
15340     // private
15341     storeOptions : function(o){
15342         o = Roo.apply({}, o);
15343         delete o.callback;
15344         delete o.scope;
15345         this.lastOptions = o;
15346     },
15347
15348     /**
15349      * Loads the Record cache from the configured Proxy using the configured Reader.
15350      * <p>
15351      * If using remote paging, then the first load call must specify the <em>start</em>
15352      * and <em>limit</em> properties in the options.params property to establish the initial
15353      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15354      * <p>
15355      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15356      * and this call will return before the new data has been loaded. Perform any post-processing
15357      * in a callback function, or in a "load" event handler.</strong>
15358      * <p>
15359      * @param {Object} options An object containing properties which control loading options:<ul>
15360      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15361      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15362      * <pre>
15363                 {
15364                     data : data,  // array of key=>value data like JsonReader
15365                     total : data.length,
15366                     success : true
15367                     
15368                 }
15369         </pre>
15370             }.</li>
15371      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15372      * passed the following arguments:<ul>
15373      * <li>r : Roo.data.Record[]</li>
15374      * <li>options: Options object from the load call</li>
15375      * <li>success: Boolean success indicator</li></ul></li>
15376      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15377      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15378      * </ul>
15379      */
15380     load : function(options){
15381         options = options || {};
15382         if(this.fireEvent("beforeload", this, options) !== false){
15383             this.storeOptions(options);
15384             var p = Roo.apply(options.params || {}, this.baseParams);
15385             // if meta was not loaded from remote source.. try requesting it.
15386             if (!this.reader.metaFromRemote) {
15387                 p._requestMeta = 1;
15388             }
15389             if(this.sortInfo && this.remoteSort){
15390                 var pn = this.paramNames;
15391                 p[pn["sort"]] = this.sortInfo.field;
15392                 p[pn["dir"]] = this.sortInfo.direction;
15393             }
15394             if (this.multiSort) {
15395                 var pn = this.paramNames;
15396                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15397             }
15398             
15399             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15400         }
15401     },
15402
15403     /**
15404      * Reloads the Record cache from the configured Proxy using the configured Reader and
15405      * the options from the last load operation performed.
15406      * @param {Object} options (optional) An object containing properties which may override the options
15407      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15408      * the most recently used options are reused).
15409      */
15410     reload : function(options){
15411         this.load(Roo.applyIf(options||{}, this.lastOptions));
15412     },
15413
15414     // private
15415     // Called as a callback by the Reader during a load operation.
15416     loadRecords : function(o, options, success){
15417          
15418         if(!o){
15419             if(success !== false){
15420                 this.fireEvent("load", this, [], options, o);
15421             }
15422             if(options.callback){
15423                 options.callback.call(options.scope || this, [], options, false);
15424             }
15425             return;
15426         }
15427         // if data returned failure - throw an exception.
15428         if (o.success === false) {
15429             // show a message if no listener is registered.
15430             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15431                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15432             }
15433             // loadmask wil be hooked into this..
15434             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15435             return;
15436         }
15437         var r = o.records, t = o.totalRecords || r.length;
15438         
15439         this.fireEvent("beforeloadadd", this, r, options, o);
15440         
15441         if(!options || options.add !== true){
15442             if(this.pruneModifiedRecords){
15443                 this.modified = [];
15444             }
15445             for(var i = 0, len = r.length; i < len; i++){
15446                 r[i].join(this);
15447             }
15448             if(this.snapshot){
15449                 this.data = this.snapshot;
15450                 delete this.snapshot;
15451             }
15452             this.data.clear();
15453             this.data.addAll(r);
15454             this.totalLength = t;
15455             this.applySort();
15456             this.fireEvent("datachanged", this);
15457         }else{
15458             this.totalLength = Math.max(t, this.data.length+r.length);
15459             this.add(r);
15460         }
15461         
15462         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15463                 
15464             var e = new Roo.data.Record({});
15465
15466             e.set(this.parent.displayField, this.parent.emptyTitle);
15467             e.set(this.parent.valueField, '');
15468
15469             this.insert(0, e);
15470         }
15471             
15472         this.fireEvent("load", this, r, options, o);
15473         if(options.callback){
15474             options.callback.call(options.scope || this, r, options, true);
15475         }
15476     },
15477
15478
15479     /**
15480      * Loads data from a passed data block. A Reader which understands the format of the data
15481      * must have been configured in the constructor.
15482      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15483      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15484      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15485      */
15486     loadData : function(o, append){
15487         var r = this.reader.readRecords(o);
15488         this.loadRecords(r, {add: append}, true);
15489     },
15490     
15491      /**
15492      * using 'cn' the nested child reader read the child array into it's child stores.
15493      * @param {Object} rec The record with a 'children array
15494      */
15495     loadDataFromChildren : function(rec)
15496     {
15497         this.loadData(this.reader.toLoadData(rec));
15498     },
15499     
15500
15501     /**
15502      * Gets the number of cached records.
15503      * <p>
15504      * <em>If using paging, this may not be the total size of the dataset. If the data object
15505      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15506      * the data set size</em>
15507      */
15508     getCount : function(){
15509         return this.data.length || 0;
15510     },
15511
15512     /**
15513      * Gets the total number of records in the dataset as returned by the server.
15514      * <p>
15515      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15516      * the dataset size</em>
15517      */
15518     getTotalCount : function(){
15519         return this.totalLength || 0;
15520     },
15521
15522     /**
15523      * Returns the sort state of the Store as an object with two properties:
15524      * <pre><code>
15525  field {String} The name of the field by which the Records are sorted
15526  direction {String} The sort order, "ASC" or "DESC"
15527      * </code></pre>
15528      */
15529     getSortState : function(){
15530         return this.sortInfo;
15531     },
15532
15533     // private
15534     applySort : function(){
15535         if(this.sortInfo && !this.remoteSort){
15536             var s = this.sortInfo, f = s.field;
15537             var st = this.fields.get(f).sortType;
15538             var fn = function(r1, r2){
15539                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15540                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15541             };
15542             this.data.sort(s.direction, fn);
15543             if(this.snapshot && this.snapshot != this.data){
15544                 this.snapshot.sort(s.direction, fn);
15545             }
15546         }
15547     },
15548
15549     /**
15550      * Sets the default sort column and order to be used by the next load operation.
15551      * @param {String} fieldName The name of the field to sort by.
15552      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15553      */
15554     setDefaultSort : function(field, dir){
15555         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15556     },
15557
15558     /**
15559      * Sort the Records.
15560      * If remote sorting is used, the sort is performed on the server, and the cache is
15561      * reloaded. If local sorting is used, the cache is sorted internally.
15562      * @param {String} fieldName The name of the field to sort by.
15563      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15564      */
15565     sort : function(fieldName, dir){
15566         var f = this.fields.get(fieldName);
15567         if(!dir){
15568             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15569             
15570             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15571                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15572             }else{
15573                 dir = f.sortDir;
15574             }
15575         }
15576         this.sortToggle[f.name] = dir;
15577         this.sortInfo = {field: f.name, direction: dir};
15578         if(!this.remoteSort){
15579             this.applySort();
15580             this.fireEvent("datachanged", this);
15581         }else{
15582             this.load(this.lastOptions);
15583         }
15584     },
15585
15586     /**
15587      * Calls the specified function for each of the Records in the cache.
15588      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15589      * Returning <em>false</em> aborts and exits the iteration.
15590      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15591      */
15592     each : function(fn, scope){
15593         this.data.each(fn, scope);
15594     },
15595
15596     /**
15597      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15598      * (e.g., during paging).
15599      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15600      */
15601     getModifiedRecords : function(){
15602         return this.modified;
15603     },
15604
15605     // private
15606     createFilterFn : function(property, value, anyMatch){
15607         if(!value.exec){ // not a regex
15608             value = String(value);
15609             if(value.length == 0){
15610                 return false;
15611             }
15612             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15613         }
15614         return function(r){
15615             return value.test(r.data[property]);
15616         };
15617     },
15618
15619     /**
15620      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15621      * @param {String} property A field on your records
15622      * @param {Number} start The record index to start at (defaults to 0)
15623      * @param {Number} end The last record index to include (defaults to length - 1)
15624      * @return {Number} The sum
15625      */
15626     sum : function(property, start, end){
15627         var rs = this.data.items, v = 0;
15628         start = start || 0;
15629         end = (end || end === 0) ? end : rs.length-1;
15630
15631         for(var i = start; i <= end; i++){
15632             v += (rs[i].data[property] || 0);
15633         }
15634         return v;
15635     },
15636
15637     /**
15638      * Filter the records by a specified property.
15639      * @param {String} field A field on your records
15640      * @param {String/RegExp} value Either a string that the field
15641      * should start with or a RegExp to test against the field
15642      * @param {Boolean} anyMatch True to match any part not just the beginning
15643      */
15644     filter : function(property, value, anyMatch){
15645         var fn = this.createFilterFn(property, value, anyMatch);
15646         return fn ? this.filterBy(fn) : this.clearFilter();
15647     },
15648
15649     /**
15650      * Filter by a function. The specified function will be called with each
15651      * record in this data source. If the function returns true the record is included,
15652      * otherwise it is filtered.
15653      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15654      * @param {Object} scope (optional) The scope of the function (defaults to this)
15655      */
15656     filterBy : function(fn, scope){
15657         this.snapshot = this.snapshot || this.data;
15658         this.data = this.queryBy(fn, scope||this);
15659         this.fireEvent("datachanged", this);
15660     },
15661
15662     /**
15663      * Query the records by a specified property.
15664      * @param {String} field A field on your records
15665      * @param {String/RegExp} value Either a string that the field
15666      * should start with or a RegExp to test against the field
15667      * @param {Boolean} anyMatch True to match any part not just the beginning
15668      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15669      */
15670     query : function(property, value, anyMatch){
15671         var fn = this.createFilterFn(property, value, anyMatch);
15672         return fn ? this.queryBy(fn) : this.data.clone();
15673     },
15674
15675     /**
15676      * Query by a function. The specified function will be called with each
15677      * record in this data source. If the function returns true the record is included
15678      * in the results.
15679      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15680      * @param {Object} scope (optional) The scope of the function (defaults to this)
15681       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15682      **/
15683     queryBy : function(fn, scope){
15684         var data = this.snapshot || this.data;
15685         return data.filterBy(fn, scope||this);
15686     },
15687
15688     /**
15689      * Collects unique values for a particular dataIndex from this store.
15690      * @param {String} dataIndex The property to collect
15691      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15692      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15693      * @return {Array} An array of the unique values
15694      **/
15695     collect : function(dataIndex, allowNull, bypassFilter){
15696         var d = (bypassFilter === true && this.snapshot) ?
15697                 this.snapshot.items : this.data.items;
15698         var v, sv, r = [], l = {};
15699         for(var i = 0, len = d.length; i < len; i++){
15700             v = d[i].data[dataIndex];
15701             sv = String(v);
15702             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15703                 l[sv] = true;
15704                 r[r.length] = v;
15705             }
15706         }
15707         return r;
15708     },
15709
15710     /**
15711      * Revert to a view of the Record cache with no filtering applied.
15712      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15713      */
15714     clearFilter : function(suppressEvent){
15715         if(this.snapshot && this.snapshot != this.data){
15716             this.data = this.snapshot;
15717             delete this.snapshot;
15718             if(suppressEvent !== true){
15719                 this.fireEvent("datachanged", this);
15720             }
15721         }
15722     },
15723
15724     // private
15725     afterEdit : function(record){
15726         if(this.modified.indexOf(record) == -1){
15727             this.modified.push(record);
15728         }
15729         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15730     },
15731     
15732     // private
15733     afterReject : function(record){
15734         this.modified.remove(record);
15735         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15736     },
15737
15738     // private
15739     afterCommit : function(record){
15740         this.modified.remove(record);
15741         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15742     },
15743
15744     /**
15745      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15746      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15747      */
15748     commitChanges : function(){
15749         var m = this.modified.slice(0);
15750         this.modified = [];
15751         for(var i = 0, len = m.length; i < len; i++){
15752             m[i].commit();
15753         }
15754     },
15755
15756     /**
15757      * Cancel outstanding changes on all changed records.
15758      */
15759     rejectChanges : function(){
15760         var m = this.modified.slice(0);
15761         this.modified = [];
15762         for(var i = 0, len = m.length; i < len; i++){
15763             m[i].reject();
15764         }
15765     },
15766
15767     onMetaChange : function(meta, rtype, o){
15768         this.recordType = rtype;
15769         this.fields = rtype.prototype.fields;
15770         delete this.snapshot;
15771         this.sortInfo = meta.sortInfo || this.sortInfo;
15772         this.modified = [];
15773         this.fireEvent('metachange', this, this.reader.meta);
15774     },
15775     
15776     moveIndex : function(data, type)
15777     {
15778         var index = this.indexOf(data);
15779         
15780         var newIndex = index + type;
15781         
15782         this.remove(data);
15783         
15784         this.insert(newIndex, data);
15785         
15786     }
15787 });/*
15788  * Based on:
15789  * Ext JS Library 1.1.1
15790  * Copyright(c) 2006-2007, Ext JS, LLC.
15791  *
15792  * Originally Released Under LGPL - original licence link has changed is not relivant.
15793  *
15794  * Fork - LGPL
15795  * <script type="text/javascript">
15796  */
15797
15798 /**
15799  * @class Roo.data.SimpleStore
15800  * @extends Roo.data.Store
15801  * Small helper class to make creating Stores from Array data easier.
15802  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15803  * @cfg {Array} fields An array of field definition objects, or field name strings.
15804  * @cfg {Object} an existing reader (eg. copied from another store)
15805  * @cfg {Array} data The multi-dimensional array of data
15806  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15807  * @cfg {Roo.data.Reader} reader  [not-required] 
15808  * @constructor
15809  * @param {Object} config
15810  */
15811 Roo.data.SimpleStore = function(config)
15812 {
15813     Roo.data.SimpleStore.superclass.constructor.call(this, {
15814         isLocal : true,
15815         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15816                 id: config.id
15817             },
15818             Roo.data.Record.create(config.fields)
15819         ),
15820         proxy : new Roo.data.MemoryProxy(config.data)
15821     });
15822     this.load();
15823 };
15824 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15825  * Based on:
15826  * Ext JS Library 1.1.1
15827  * Copyright(c) 2006-2007, Ext JS, LLC.
15828  *
15829  * Originally Released Under LGPL - original licence link has changed is not relivant.
15830  *
15831  * Fork - LGPL
15832  * <script type="text/javascript">
15833  */
15834
15835 /**
15836 /**
15837  * @extends Roo.data.Store
15838  * @class Roo.data.JsonStore
15839  * Small helper class to make creating Stores for JSON data easier. <br/>
15840 <pre><code>
15841 var store = new Roo.data.JsonStore({
15842     url: 'get-images.php',
15843     root: 'images',
15844     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15845 });
15846 </code></pre>
15847  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15848  * JsonReader and HttpProxy (unless inline data is provided).</b>
15849  * @cfg {Array} fields An array of field definition objects, or field name strings.
15850  * @constructor
15851  * @param {Object} config
15852  */
15853 Roo.data.JsonStore = function(c){
15854     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15855         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15856         reader: new Roo.data.JsonReader(c, c.fields)
15857     }));
15858 };
15859 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15860  * Based on:
15861  * Ext JS Library 1.1.1
15862  * Copyright(c) 2006-2007, Ext JS, LLC.
15863  *
15864  * Originally Released Under LGPL - original licence link has changed is not relivant.
15865  *
15866  * Fork - LGPL
15867  * <script type="text/javascript">
15868  */
15869
15870  
15871 Roo.data.Field = function(config){
15872     if(typeof config == "string"){
15873         config = {name: config};
15874     }
15875     Roo.apply(this, config);
15876     
15877     if(!this.type){
15878         this.type = "auto";
15879     }
15880     
15881     var st = Roo.data.SortTypes;
15882     // named sortTypes are supported, here we look them up
15883     if(typeof this.sortType == "string"){
15884         this.sortType = st[this.sortType];
15885     }
15886     
15887     // set default sortType for strings and dates
15888     if(!this.sortType){
15889         switch(this.type){
15890             case "string":
15891                 this.sortType = st.asUCString;
15892                 break;
15893             case "date":
15894                 this.sortType = st.asDate;
15895                 break;
15896             default:
15897                 this.sortType = st.none;
15898         }
15899     }
15900
15901     // define once
15902     var stripRe = /[\$,%]/g;
15903
15904     // prebuilt conversion function for this field, instead of
15905     // switching every time we're reading a value
15906     if(!this.convert){
15907         var cv, dateFormat = this.dateFormat;
15908         switch(this.type){
15909             case "":
15910             case "auto":
15911             case undefined:
15912                 cv = function(v){ return v; };
15913                 break;
15914             case "string":
15915                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15916                 break;
15917             case "int":
15918                 cv = function(v){
15919                     return v !== undefined && v !== null && v !== '' ?
15920                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15921                     };
15922                 break;
15923             case "float":
15924                 cv = function(v){
15925                     return v !== undefined && v !== null && v !== '' ?
15926                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15927                     };
15928                 break;
15929             case "bool":
15930             case "boolean":
15931                 cv = function(v){ return v === true || v === "true" || v == 1; };
15932                 break;
15933             case "date":
15934                 cv = function(v){
15935                     if(!v){
15936                         return '';
15937                     }
15938                     if(v instanceof Date){
15939                         return v;
15940                     }
15941                     if(dateFormat){
15942                         if(dateFormat == "timestamp"){
15943                             return new Date(v*1000);
15944                         }
15945                         return Date.parseDate(v, dateFormat);
15946                     }
15947                     var parsed = Date.parse(v);
15948                     return parsed ? new Date(parsed) : null;
15949                 };
15950              break;
15951             
15952         }
15953         this.convert = cv;
15954     }
15955 };
15956
15957 Roo.data.Field.prototype = {
15958     dateFormat: null,
15959     defaultValue: "",
15960     mapping: null,
15961     sortType : null,
15962     sortDir : "ASC"
15963 };/*
15964  * Based on:
15965  * Ext JS Library 1.1.1
15966  * Copyright(c) 2006-2007, Ext JS, LLC.
15967  *
15968  * Originally Released Under LGPL - original licence link has changed is not relivant.
15969  *
15970  * Fork - LGPL
15971  * <script type="text/javascript">
15972  */
15973  
15974 // Base class for reading structured data from a data source.  This class is intended to be
15975 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15976
15977 /**
15978  * @class Roo.data.DataReader
15979  * @abstract
15980  * Base class for reading structured data from a data source.  This class is intended to be
15981  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15982  */
15983
15984 Roo.data.DataReader = function(meta, recordType){
15985     
15986     this.meta = meta;
15987     
15988     this.recordType = recordType instanceof Array ? 
15989         Roo.data.Record.create(recordType) : recordType;
15990 };
15991
15992 Roo.data.DataReader.prototype = {
15993     
15994     
15995     readerType : 'Data',
15996      /**
15997      * Create an empty record
15998      * @param {Object} data (optional) - overlay some values
15999      * @return {Roo.data.Record} record created.
16000      */
16001     newRow :  function(d) {
16002         var da =  {};
16003         this.recordType.prototype.fields.each(function(c) {
16004             switch( c.type) {
16005                 case 'int' : da[c.name] = 0; break;
16006                 case 'date' : da[c.name] = new Date(); break;
16007                 case 'float' : da[c.name] = 0.0; break;
16008                 case 'boolean' : da[c.name] = false; break;
16009                 default : da[c.name] = ""; break;
16010             }
16011             
16012         });
16013         return new this.recordType(Roo.apply(da, d));
16014     }
16015     
16016     
16017 };/*
16018  * Based on:
16019  * Ext JS Library 1.1.1
16020  * Copyright(c) 2006-2007, Ext JS, LLC.
16021  *
16022  * Originally Released Under LGPL - original licence link has changed is not relivant.
16023  *
16024  * Fork - LGPL
16025  * <script type="text/javascript">
16026  */
16027
16028 /**
16029  * @class Roo.data.DataProxy
16030  * @extends Roo.util.Observable
16031  * @abstract
16032  * This class is an abstract base class for implementations which provide retrieval of
16033  * unformatted data objects.<br>
16034  * <p>
16035  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16036  * (of the appropriate type which knows how to parse the data object) to provide a block of
16037  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16038  * <p>
16039  * Custom implementations must implement the load method as described in
16040  * {@link Roo.data.HttpProxy#load}.
16041  */
16042 Roo.data.DataProxy = function(){
16043     this.addEvents({
16044         /**
16045          * @event beforeload
16046          * Fires before a network request is made to retrieve a data object.
16047          * @param {Object} This DataProxy object.
16048          * @param {Object} params The params parameter to the load function.
16049          */
16050         beforeload : true,
16051         /**
16052          * @event load
16053          * Fires before the load method's callback is called.
16054          * @param {Object} This DataProxy object.
16055          * @param {Object} o The data object.
16056          * @param {Object} arg The callback argument object passed to the load function.
16057          */
16058         load : true,
16059         /**
16060          * @event loadexception
16061          * Fires if an Exception occurs during data retrieval.
16062          * @param {Object} This DataProxy object.
16063          * @param {Object} o The data object.
16064          * @param {Object} arg The callback argument object passed to the load function.
16065          * @param {Object} e The Exception.
16066          */
16067         loadexception : true
16068     });
16069     Roo.data.DataProxy.superclass.constructor.call(this);
16070 };
16071
16072 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16073
16074     /**
16075      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16076      */
16077 /*
16078  * Based on:
16079  * Ext JS Library 1.1.1
16080  * Copyright(c) 2006-2007, Ext JS, LLC.
16081  *
16082  * Originally Released Under LGPL - original licence link has changed is not relivant.
16083  *
16084  * Fork - LGPL
16085  * <script type="text/javascript">
16086  */
16087 /**
16088  * @class Roo.data.MemoryProxy
16089  * @extends Roo.data.DataProxy
16090  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16091  * to the Reader when its load method is called.
16092  * @constructor
16093  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16094  */
16095 Roo.data.MemoryProxy = function(data){
16096     if (typeof(data) != 'undefined' && typeof(data.data) != 'undefined') {
16097         data = data.data;
16098     }
16099     Roo.data.MemoryProxy.superclass.constructor.call(this);
16100     this.data = data;
16101 };
16102
16103 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16104     
16105     /**
16106      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16107      */
16108     /**
16109      * Load data from the requested source (in this case an in-memory
16110      * data object passed to the constructor), read the data object into
16111      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16112      * process that block using the passed callback.
16113      * @param {Object} params This parameter is not used by the MemoryProxy class.
16114      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16115      * object into a block of Roo.data.Records.
16116      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16117      * The function must be passed <ul>
16118      * <li>The Record block object</li>
16119      * <li>The "arg" argument from the load function</li>
16120      * <li>A boolean success indicator</li>
16121      * </ul>
16122      * @param {Object} scope The scope in which to call the callback
16123      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16124      */
16125     load : function(params, reader, callback, scope, arg){
16126         params = params || {};
16127         var result;
16128         try {
16129             result = reader.readRecords(params.data ? params.data :this.data);
16130         }catch(e){
16131             this.fireEvent("loadexception", this, arg, null, e);
16132             callback.call(scope, null, arg, false);
16133             return;
16134         }
16135         callback.call(scope, result, arg, true);
16136     },
16137     
16138     // private
16139     update : function(params, records){
16140         
16141     }
16142 });/*
16143  * Based on:
16144  * Ext JS Library 1.1.1
16145  * Copyright(c) 2006-2007, Ext JS, LLC.
16146  *
16147  * Originally Released Under LGPL - original licence link has changed is not relivant.
16148  *
16149  * Fork - LGPL
16150  * <script type="text/javascript">
16151  */
16152 /**
16153  * @class Roo.data.HttpProxy
16154  * @extends Roo.data.DataProxy
16155  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16156  * configured to reference a certain URL.<br><br>
16157  * <p>
16158  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16159  * from which the running page was served.<br><br>
16160  * <p>
16161  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16162  * <p>
16163  * Be aware that to enable the browser to parse an XML document, the server must set
16164  * the Content-Type header in the HTTP response to "text/xml".
16165  * @constructor
16166  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16167  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16168  * will be used to make the request.
16169  */
16170 Roo.data.HttpProxy = function(conn){
16171     Roo.data.HttpProxy.superclass.constructor.call(this);
16172     // is conn a conn config or a real conn?
16173     this.conn = conn;
16174     this.useAjax = !conn || !conn.events;
16175   
16176 };
16177
16178 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16179     // thse are take from connection...
16180     
16181     /**
16182      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16183      */
16184     /**
16185      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16186      * extra parameters to each request made by this object. (defaults to undefined)
16187      */
16188     /**
16189      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16190      *  to each request made by this object. (defaults to undefined)
16191      */
16192     /**
16193      * @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)
16194      */
16195     /**
16196      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16197      */
16198      /**
16199      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16200      * @type Boolean
16201      */
16202   
16203
16204     /**
16205      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16206      * @type Boolean
16207      */
16208     /**
16209      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16210      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16211      * a finer-grained basis than the DataProxy events.
16212      */
16213     getConnection : function(){
16214         return this.useAjax ? Roo.Ajax : this.conn;
16215     },
16216
16217     /**
16218      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16219      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16220      * process that block using the passed callback.
16221      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16222      * for the request to the remote server.
16223      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16224      * object into a block of Roo.data.Records.
16225      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16226      * The function must be passed <ul>
16227      * <li>The Record block object</li>
16228      * <li>The "arg" argument from the load function</li>
16229      * <li>A boolean success indicator</li>
16230      * </ul>
16231      * @param {Object} scope The scope in which to call the callback
16232      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16233      */
16234     load : function(params, reader, callback, scope, arg){
16235         if(this.fireEvent("beforeload", this, params) !== false){
16236             var  o = {
16237                 params : params || {},
16238                 request: {
16239                     callback : callback,
16240                     scope : scope,
16241                     arg : arg
16242                 },
16243                 reader: reader,
16244                 callback : this.loadResponse,
16245                 scope: this
16246             };
16247             if(this.useAjax){
16248                 Roo.applyIf(o, this.conn);
16249                 if(this.activeRequest){
16250                     Roo.Ajax.abort(this.activeRequest);
16251                 }
16252                 this.activeRequest = Roo.Ajax.request(o);
16253             }else{
16254                 this.conn.request(o);
16255             }
16256         }else{
16257             callback.call(scope||this, null, arg, false);
16258         }
16259     },
16260
16261     // private
16262     loadResponse : function(o, success, response){
16263         delete this.activeRequest;
16264         if(!success){
16265             this.fireEvent("loadexception", this, o, response);
16266             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16267             return;
16268         }
16269         var result;
16270         try {
16271             result = o.reader.read(response);
16272         }catch(e){
16273             o.success = false;
16274             o.raw = { errorMsg : response.responseText };
16275             this.fireEvent("loadexception", this, o, response, e);
16276             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16277             return;
16278         }
16279         
16280         this.fireEvent("load", this, o, o.request.arg);
16281         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16282     },
16283
16284     // private
16285     update : function(dataSet){
16286
16287     },
16288
16289     // private
16290     updateResponse : function(dataSet){
16291
16292     }
16293 });/*
16294  * Based on:
16295  * Ext JS Library 1.1.1
16296  * Copyright(c) 2006-2007, Ext JS, LLC.
16297  *
16298  * Originally Released Under LGPL - original licence link has changed is not relivant.
16299  *
16300  * Fork - LGPL
16301  * <script type="text/javascript">
16302  */
16303
16304 /**
16305  * @class Roo.data.ScriptTagProxy
16306  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16307  * other than the originating domain of the running page.<br><br>
16308  * <p>
16309  * <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
16310  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16311  * <p>
16312  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16313  * source code that is used as the source inside a &lt;script> tag.<br><br>
16314  * <p>
16315  * In order for the browser to process the returned data, the server must wrap the data object
16316  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16317  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16318  * depending on whether the callback name was passed:
16319  * <p>
16320  * <pre><code>
16321 boolean scriptTag = false;
16322 String cb = request.getParameter("callback");
16323 if (cb != null) {
16324     scriptTag = true;
16325     response.setContentType("text/javascript");
16326 } else {
16327     response.setContentType("application/x-json");
16328 }
16329 Writer out = response.getWriter();
16330 if (scriptTag) {
16331     out.write(cb + "(");
16332 }
16333 out.print(dataBlock.toJsonString());
16334 if (scriptTag) {
16335     out.write(");");
16336 }
16337 </pre></code>
16338  *
16339  * @constructor
16340  * @param {Object} config A configuration object.
16341  */
16342 Roo.data.ScriptTagProxy = function(config){
16343     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16344     Roo.apply(this, config);
16345     this.head = document.getElementsByTagName("head")[0];
16346 };
16347
16348 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16349
16350 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16351     /**
16352      * @cfg {String} url The URL from which to request the data object.
16353      */
16354     /**
16355      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16356      */
16357     timeout : 30000,
16358     /**
16359      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16360      * the server the name of the callback function set up by the load call to process the returned data object.
16361      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16362      * javascript output which calls this named function passing the data object as its only parameter.
16363      */
16364     callbackParam : "callback",
16365     /**
16366      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16367      * name to the request.
16368      */
16369     nocache : true,
16370
16371     /**
16372      * Load data from the configured URL, read the data object into
16373      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16374      * process that block using the passed callback.
16375      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16376      * for the request to the remote server.
16377      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16378      * object into a block of Roo.data.Records.
16379      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16380      * The function must be passed <ul>
16381      * <li>The Record block object</li>
16382      * <li>The "arg" argument from the load function</li>
16383      * <li>A boolean success indicator</li>
16384      * </ul>
16385      * @param {Object} scope The scope in which to call the callback
16386      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16387      */
16388     load : function(params, reader, callback, scope, arg){
16389         if(this.fireEvent("beforeload", this, params) !== false){
16390
16391             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16392
16393             var url = this.url;
16394             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16395             if(this.nocache){
16396                 url += "&_dc=" + (new Date().getTime());
16397             }
16398             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16399             var trans = {
16400                 id : transId,
16401                 cb : "stcCallback"+transId,
16402                 scriptId : "stcScript"+transId,
16403                 params : params,
16404                 arg : arg,
16405                 url : url,
16406                 callback : callback,
16407                 scope : scope,
16408                 reader : reader
16409             };
16410             var conn = this;
16411
16412             window[trans.cb] = function(o){
16413                 conn.handleResponse(o, trans);
16414             };
16415
16416             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16417
16418             if(this.autoAbort !== false){
16419                 this.abort();
16420             }
16421
16422             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16423
16424             var script = document.createElement("script");
16425             script.setAttribute("src", url);
16426             script.setAttribute("type", "text/javascript");
16427             script.setAttribute("id", trans.scriptId);
16428             this.head.appendChild(script);
16429
16430             this.trans = trans;
16431         }else{
16432             callback.call(scope||this, null, arg, false);
16433         }
16434     },
16435
16436     // private
16437     isLoading : function(){
16438         return this.trans ? true : false;
16439     },
16440
16441     /**
16442      * Abort the current server request.
16443      */
16444     abort : function(){
16445         if(this.isLoading()){
16446             this.destroyTrans(this.trans);
16447         }
16448     },
16449
16450     // private
16451     destroyTrans : function(trans, isLoaded){
16452         this.head.removeChild(document.getElementById(trans.scriptId));
16453         clearTimeout(trans.timeoutId);
16454         if(isLoaded){
16455             window[trans.cb] = undefined;
16456             try{
16457                 delete window[trans.cb];
16458             }catch(e){}
16459         }else{
16460             // if hasn't been loaded, wait for load to remove it to prevent script error
16461             window[trans.cb] = function(){
16462                 window[trans.cb] = undefined;
16463                 try{
16464                     delete window[trans.cb];
16465                 }catch(e){}
16466             };
16467         }
16468     },
16469
16470     // private
16471     handleResponse : function(o, trans){
16472         this.trans = false;
16473         this.destroyTrans(trans, true);
16474         var result;
16475         try {
16476             result = trans.reader.readRecords(o);
16477         }catch(e){
16478             this.fireEvent("loadexception", this, o, trans.arg, e);
16479             trans.callback.call(trans.scope||window, null, trans.arg, false);
16480             return;
16481         }
16482         this.fireEvent("load", this, o, trans.arg);
16483         trans.callback.call(trans.scope||window, result, trans.arg, true);
16484     },
16485
16486     // private
16487     handleFailure : function(trans){
16488         this.trans = false;
16489         this.destroyTrans(trans, false);
16490         this.fireEvent("loadexception", this, null, trans.arg);
16491         trans.callback.call(trans.scope||window, null, trans.arg, false);
16492     }
16493 });/*
16494  * Based on:
16495  * Ext JS Library 1.1.1
16496  * Copyright(c) 2006-2007, Ext JS, LLC.
16497  *
16498  * Originally Released Under LGPL - original licence link has changed is not relivant.
16499  *
16500  * Fork - LGPL
16501  * <script type="text/javascript">
16502  */
16503
16504 /**
16505  * @class Roo.data.JsonReader
16506  * @extends Roo.data.DataReader
16507  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16508  * based on mappings in a provided Roo.data.Record constructor.
16509  * 
16510  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16511  * in the reply previously. 
16512  * 
16513  * <p>
16514  * Example code:
16515  * <pre><code>
16516 var RecordDef = Roo.data.Record.create([
16517     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16518     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16519 ]);
16520 var myReader = new Roo.data.JsonReader({
16521     totalProperty: "results",    // The property which contains the total dataset size (optional)
16522     root: "rows",                // The property which contains an Array of row objects
16523     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16524 }, RecordDef);
16525 </code></pre>
16526  * <p>
16527  * This would consume a JSON file like this:
16528  * <pre><code>
16529 { 'results': 2, 'rows': [
16530     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16531     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16532 }
16533 </code></pre>
16534  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16535  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16536  * paged from the remote server.
16537  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16538  * @cfg {String} root name of the property which contains the Array of row objects.
16539  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16540  * @cfg {Array} fields Array of field definition objects
16541  * @constructor
16542  * Create a new JsonReader
16543  * @param {Object} meta Metadata configuration options
16544  * @param {Object} recordType Either an Array of field definition objects,
16545  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16546  */
16547 Roo.data.JsonReader = function(meta, recordType){
16548     
16549     meta = meta || {};
16550     // set some defaults:
16551     Roo.applyIf(meta, {
16552         totalProperty: 'total',
16553         successProperty : 'success',
16554         root : 'data',
16555         id : 'id'
16556     });
16557     
16558     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16559 };
16560 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16561     
16562     readerType : 'Json',
16563     
16564     /**
16565      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16566      * Used by Store query builder to append _requestMeta to params.
16567      * 
16568      */
16569     metaFromRemote : false,
16570     /**
16571      * This method is only used by a DataProxy which has retrieved data from a remote server.
16572      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16573      * @return {Object} data A data block which is used by an Roo.data.Store object as
16574      * a cache of Roo.data.Records.
16575      */
16576     read : function(response){
16577         var json = response.responseText;
16578        
16579         var o = /* eval:var:o */ eval("("+json+")");
16580         if(!o) {
16581             throw {message: "JsonReader.read: Json object not found"};
16582         }
16583         
16584         if(o.metaData){
16585             
16586             delete this.ef;
16587             this.metaFromRemote = true;
16588             this.meta = o.metaData;
16589             this.recordType = Roo.data.Record.create(o.metaData.fields);
16590             this.onMetaChange(this.meta, this.recordType, o);
16591         }
16592         return this.readRecords(o);
16593     },
16594
16595     // private function a store will implement
16596     onMetaChange : function(meta, recordType, o){
16597
16598     },
16599
16600     /**
16601          * @ignore
16602          */
16603     simpleAccess: function(obj, subsc) {
16604         return obj[subsc];
16605     },
16606
16607         /**
16608          * @ignore
16609          */
16610     getJsonAccessor: function(){
16611         var re = /[\[\.]/;
16612         return function(expr) {
16613             try {
16614                 return(re.test(expr))
16615                     ? new Function("obj", "return obj." + expr)
16616                     : function(obj){
16617                         return obj[expr];
16618                     };
16619             } catch(e){}
16620             return Roo.emptyFn;
16621         };
16622     }(),
16623
16624     /**
16625      * Create a data block containing Roo.data.Records from an XML document.
16626      * @param {Object} o An object which contains an Array of row objects in the property specified
16627      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16628      * which contains the total size of the dataset.
16629      * @return {Object} data A data block which is used by an Roo.data.Store object as
16630      * a cache of Roo.data.Records.
16631      */
16632     readRecords : function(o){
16633         /**
16634          * After any data loads, the raw JSON data is available for further custom processing.
16635          * @type Object
16636          */
16637         this.o = o;
16638         var s = this.meta, Record = this.recordType,
16639             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16640
16641 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16642         if (!this.ef) {
16643             if(s.totalProperty) {
16644                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16645                 }
16646                 if(s.successProperty) {
16647                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16648                 }
16649                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16650                 if (s.id) {
16651                         var g = this.getJsonAccessor(s.id);
16652                         this.getId = function(rec) {
16653                                 var r = g(rec);  
16654                                 return (r === undefined || r === "") ? null : r;
16655                         };
16656                 } else {
16657                         this.getId = function(){return null;};
16658                 }
16659             this.ef = [];
16660             for(var jj = 0; jj < fl; jj++){
16661                 f = fi[jj];
16662                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16663                 this.ef[jj] = this.getJsonAccessor(map);
16664             }
16665         }
16666
16667         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16668         if(s.totalProperty){
16669             var vt = parseInt(this.getTotal(o), 10);
16670             if(!isNaN(vt)){
16671                 totalRecords = vt;
16672             }
16673         }
16674         if(s.successProperty){
16675             var vs = this.getSuccess(o);
16676             if(vs === false || vs === 'false'){
16677                 success = false;
16678             }
16679         }
16680         var records = [];
16681         for(var i = 0; i < c; i++){
16682             var n = root[i];
16683             var values = {};
16684             var id = this.getId(n);
16685             for(var j = 0; j < fl; j++){
16686                 f = fi[j];
16687                                 var v = this.ef[j](n);
16688                                 if (!f.convert) {
16689                                         Roo.log('missing convert for ' + f.name);
16690                                         Roo.log(f);
16691                                         continue;
16692                                 }
16693                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16694             }
16695                         if (!Record) {
16696                                 return {
16697                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16698                                         success : false,
16699                                         records : [],
16700                                         totalRecords : 0
16701                                 };
16702                         }
16703             var record = new Record(values, id);
16704             record.json = n;
16705             records[i] = record;
16706         }
16707         return {
16708             raw : o,
16709             success : success,
16710             records : records,
16711             totalRecords : totalRecords
16712         };
16713     },
16714     // used when loading children.. @see loadDataFromChildren
16715     toLoadData: function(rec)
16716     {
16717         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16718         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16719         return { data : data, total : data.length };
16720         
16721     }
16722 });/*
16723  * Based on:
16724  * Ext JS Library 1.1.1
16725  * Copyright(c) 2006-2007, Ext JS, LLC.
16726  *
16727  * Originally Released Under LGPL - original licence link has changed is not relivant.
16728  *
16729  * Fork - LGPL
16730  * <script type="text/javascript">
16731  */
16732
16733 /**
16734  * @class Roo.data.ArrayReader
16735  * @extends Roo.data.DataReader
16736  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16737  * Each element of that Array represents a row of data fields. The
16738  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16739  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16740  * <p>
16741  * Example code:.
16742  * <pre><code>
16743 var RecordDef = Roo.data.Record.create([
16744     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16745     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16746 ]);
16747 var myReader = new Roo.data.ArrayReader({
16748     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16749 }, RecordDef);
16750 </code></pre>
16751  * <p>
16752  * This would consume an Array like this:
16753  * <pre><code>
16754 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16755   </code></pre>
16756  
16757  * @constructor
16758  * Create a new JsonReader
16759  * @param {Object} meta Metadata configuration options.
16760  * @param {Object|Array} recordType Either an Array of field definition objects
16761  * 
16762  * @cfg {Array} fields Array of field definition objects
16763  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16764  * as specified to {@link Roo.data.Record#create},
16765  * or an {@link Roo.data.Record} object
16766  *
16767  * 
16768  * created using {@link Roo.data.Record#create}.
16769  */
16770 Roo.data.ArrayReader = function(meta, recordType)
16771 {    
16772     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16773 };
16774
16775 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16776     
16777       /**
16778      * Create a data block containing Roo.data.Records from an XML document.
16779      * @param {Object} o An Array of row objects which represents the dataset.
16780      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16781      * a cache of Roo.data.Records.
16782      */
16783     readRecords : function(o)
16784     {
16785         var sid = this.meta ? this.meta.id : null;
16786         var recordType = this.recordType, fields = recordType.prototype.fields;
16787         var records = [];
16788         var root = o;
16789         for(var i = 0; i < root.length; i++){
16790             var n = root[i];
16791             var values = {};
16792             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16793             for(var j = 0, jlen = fields.length; j < jlen; j++){
16794                 var f = fields.items[j];
16795                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16796                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16797                 v = f.convert(v);
16798                 values[f.name] = v;
16799             }
16800             var record = new recordType(values, id);
16801             record.json = n;
16802             records[records.length] = record;
16803         }
16804         return {
16805             records : records,
16806             totalRecords : records.length
16807         };
16808     },
16809     // used when loading children.. @see loadDataFromChildren
16810     toLoadData: function(rec)
16811     {
16812         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16813         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16814         
16815     }
16816     
16817     
16818 });/*
16819  * - LGPL
16820  * * 
16821  */
16822
16823 /**
16824  * @class Roo.bootstrap.form.ComboBox
16825  * @extends Roo.bootstrap.form.TriggerField
16826  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16827  * @cfg {Boolean} append (true|false) default false
16828  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16829  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16830  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16831  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16832  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16833  * @cfg {Boolean} animate default true
16834  * @cfg {Boolean} emptyResultText only for touch device
16835  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16836  * @cfg {String} emptyTitle default ''
16837  * @cfg {Number} width fixed with? experimental
16838  * @constructor
16839  * Create a new ComboBox.
16840  * @param {Object} config Configuration options
16841  */
16842 Roo.bootstrap.form.ComboBox = function(config){
16843     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16844     this.addEvents({
16845         /**
16846          * @event expand
16847          * Fires when the dropdown list is expanded
16848         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16849         */
16850         'expand' : true,
16851         /**
16852          * @event collapse
16853          * Fires when the dropdown list is collapsed
16854         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16855         */
16856         'collapse' : true,
16857         /**
16858          * @event beforeselect
16859          * Fires before a list item is selected. Return false to cancel the selection.
16860         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16861         * @param {Roo.data.Record} record The data record returned from the underlying store
16862         * @param {Number} index The index of the selected item in the dropdown list
16863         */
16864         'beforeselect' : true,
16865         /**
16866          * @event select
16867          * Fires when a list item is selected
16868         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16869         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16870         * @param {Number} index The index of the selected item in the dropdown list
16871         */
16872         'select' : true,
16873         /**
16874          * @event beforequery
16875          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16876          * The event object passed has these properties:
16877         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16878         * @param {String} query The query
16879         * @param {Boolean} forceAll true to force "all" query
16880         * @param {Boolean} cancel true to cancel the query
16881         * @param {Object} e The query event object
16882         */
16883         'beforequery': true,
16884          /**
16885          * @event add
16886          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16887         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16888         */
16889         'add' : true,
16890         /**
16891          * @event edit
16892          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16893         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16894         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16895         */
16896         'edit' : true,
16897         /**
16898          * @event remove
16899          * Fires when the remove value from the combobox array
16900         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16901         */
16902         'remove' : true,
16903         /**
16904          * @event afterremove
16905          * Fires when the remove value from the combobox array
16906         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16907         */
16908         'afterremove' : true,
16909         /**
16910          * @event specialfilter
16911          * Fires when specialfilter
16912             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16913             */
16914         'specialfilter' : true,
16915         /**
16916          * @event tick
16917          * Fires when tick the element
16918             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16919             */
16920         'tick' : true,
16921         /**
16922          * @event touchviewdisplay
16923          * Fires when touch view require special display (default is using displayField)
16924             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16925             * @param {Object} cfg set html .
16926             */
16927         'touchviewdisplay' : true
16928         
16929     });
16930     
16931     this.item = [];
16932     this.tickItems = [];
16933     
16934     this.selectedIndex = -1;
16935     if(this.mode == 'local'){
16936         if(config.queryDelay === undefined){
16937             this.queryDelay = 10;
16938         }
16939         if(config.minChars === undefined){
16940             this.minChars = 0;
16941         }
16942     }
16943 };
16944
16945 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16946      
16947     /**
16948      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16949      * rendering into an Roo.Editor, defaults to false)
16950      */
16951     /**
16952      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16953      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16954      */
16955     /**
16956      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16957      */
16958     /**
16959      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16960      * the dropdown list (defaults to undefined, with no header element)
16961      */
16962
16963      /**
16964      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16965      */
16966      
16967      /**
16968      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16969      */
16970     listWidth: undefined,
16971     /**
16972      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16973      * mode = 'remote' or 'text' if mode = 'local')
16974      */
16975     displayField: undefined,
16976     
16977     /**
16978      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16979      * mode = 'remote' or 'value' if mode = 'local'). 
16980      * Note: use of a valueField requires the user make a selection
16981      * in order for a value to be mapped.
16982      */
16983     valueField: undefined,
16984     /**
16985      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16986      */
16987     modalTitle : '',
16988     
16989     /**
16990      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16991      * field's data value (defaults to the underlying DOM element's name)
16992      */
16993     hiddenName: undefined,
16994     /**
16995      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16996      */
16997     listClass: '',
16998     /**
16999      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17000      */
17001     selectedClass: 'active',
17002     
17003     /**
17004      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17005      */
17006     shadow:'sides',
17007     /**
17008      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17009      * anchor positions (defaults to 'tl-bl')
17010      */
17011     listAlign: 'tl-bl?',
17012     /**
17013      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17014      */
17015     maxHeight: 300,
17016     /**
17017      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17018      * query specified by the allQuery config option (defaults to 'query')
17019      */
17020     triggerAction: 'query',
17021     /**
17022      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17023      * (defaults to 4, does not apply if editable = false)
17024      */
17025     minChars : 4,
17026     /**
17027      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17028      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17029      */
17030     typeAhead: false,
17031     /**
17032      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17033      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17034      */
17035     queryDelay: 500,
17036     /**
17037      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17038      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17039      */
17040     pageSize: 0,
17041     /**
17042      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17043      * when editable = true (defaults to false)
17044      */
17045     selectOnFocus:false,
17046     /**
17047      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17048      */
17049     queryParam: 'query',
17050     /**
17051      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17052      * when mode = 'remote' (defaults to 'Loading...')
17053      */
17054     loadingText: 'Loading...',
17055     /**
17056      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17057      */
17058     resizable: false,
17059     /**
17060      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17061      */
17062     handleHeight : 8,
17063     /**
17064      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17065      * traditional select (defaults to true)
17066      */
17067     editable: true,
17068     /**
17069      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17070      */
17071     allQuery: '',
17072     /**
17073      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17074      */
17075     mode: 'remote',
17076     /**
17077      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17078      * listWidth has a higher value)
17079      */
17080     minListWidth : 70,
17081     /**
17082      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17083      * allow the user to set arbitrary text into the field (defaults to false)
17084      */
17085     forceSelection:false,
17086     /**
17087      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17088      * if typeAhead = true (defaults to 250)
17089      */
17090     typeAheadDelay : 250,
17091     /**
17092      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17093      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17094      */
17095     valueNotFoundText : undefined,
17096     /**
17097      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17098      */
17099     blockFocus : false,
17100     
17101     /**
17102      * @cfg {Boolean} disableClear Disable showing of clear button.
17103      */
17104     disableClear : false,
17105     /**
17106      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17107      */
17108     alwaysQuery : false,
17109     
17110     /**
17111      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17112      */
17113     multiple : false,
17114     
17115     /**
17116      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17117      */
17118     invalidClass : "has-warning",
17119     
17120     /**
17121      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17122      */
17123     validClass : "has-success",
17124     
17125     /**
17126      * @cfg {Boolean} specialFilter (true|false) special filter default false
17127      */
17128     specialFilter : false,
17129     
17130     /**
17131      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17132      */
17133     mobileTouchView : true,
17134     
17135     /**
17136      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17137      */
17138     useNativeIOS : false,
17139     
17140     /**
17141      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17142      */
17143     mobile_restrict_height : false,
17144     
17145     ios_options : false,
17146     
17147     //private
17148     addicon : false,
17149     editicon: false,
17150     
17151     page: 0,
17152     hasQuery: false,
17153     append: false,
17154     loadNext: false,
17155     autoFocus : true,
17156     tickable : false,
17157     btnPosition : 'right',
17158     triggerList : true,
17159     showToggleBtn : true,
17160     animate : true,
17161     emptyResultText: 'Empty',
17162     triggerText : 'Select',
17163     emptyTitle : '',
17164     width : false,
17165     
17166     // element that contains real text value.. (when hidden is used..)
17167     
17168     getAutoCreate : function()
17169     {   
17170         var cfg = false;
17171         //render
17172         /*
17173          * Render classic select for iso
17174          */
17175         
17176         if(Roo.isIOS && this.useNativeIOS){
17177             cfg = this.getAutoCreateNativeIOS();
17178             return cfg;
17179         }
17180         
17181         /*
17182          * Touch Devices
17183          */
17184         
17185         if(Roo.isTouch && this.mobileTouchView){
17186             cfg = this.getAutoCreateTouchView();
17187             return cfg;;
17188         }
17189         
17190         /*
17191          *  Normal ComboBox
17192          */
17193         if(!this.tickable){
17194             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17195             return cfg;
17196         }
17197         
17198         /*
17199          *  ComboBox with tickable selections
17200          */
17201              
17202         var align = this.labelAlign || this.parentLabelAlign();
17203         
17204         cfg = {
17205             cls : 'form-group roo-combobox-tickable' //input-group
17206         };
17207         
17208         var btn_text_select = '';
17209         var btn_text_done = '';
17210         var btn_text_cancel = '';
17211         
17212         if (this.btn_text_show) {
17213             btn_text_select = 'Select';
17214             btn_text_done = 'Done';
17215             btn_text_cancel = 'Cancel'; 
17216         }
17217         
17218         var buttons = {
17219             tag : 'div',
17220             cls : 'tickable-buttons',
17221             cn : [
17222                 {
17223                     tag : 'button',
17224                     type : 'button',
17225                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17226                     //html : this.triggerText
17227                     html: btn_text_select
17228                 },
17229                 {
17230                     tag : 'button',
17231                     type : 'button',
17232                     name : 'ok',
17233                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17234                     //html : 'Done'
17235                     html: btn_text_done
17236                 },
17237                 {
17238                     tag : 'button',
17239                     type : 'button',
17240                     name : 'cancel',
17241                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17242                     //html : 'Cancel'
17243                     html: btn_text_cancel
17244                 }
17245             ]
17246         };
17247         
17248         if(this.editable){
17249             buttons.cn.unshift({
17250                 tag: 'input',
17251                 cls: 'roo-select2-search-field-input'
17252             });
17253         }
17254         
17255         var _this = this;
17256         
17257         Roo.each(buttons.cn, function(c){
17258             if (_this.size) {
17259                 c.cls += ' btn-' + _this.size;
17260             }
17261
17262             if (_this.disabled) {
17263                 c.disabled = true;
17264             }
17265         });
17266         
17267         var box = {
17268             tag: 'div',
17269             style : 'display: contents',
17270             cn: [
17271                 {
17272                     tag: 'input',
17273                     type : 'hidden',
17274                     cls: 'form-hidden-field'
17275                 },
17276                 {
17277                     tag: 'ul',
17278                     cls: 'roo-select2-choices',
17279                     cn:[
17280                         {
17281                             tag: 'li',
17282                             cls: 'roo-select2-search-field',
17283                             cn: [
17284                                 buttons
17285                             ]
17286                         }
17287                     ]
17288                 }
17289             ]
17290         };
17291         
17292         var combobox = {
17293             cls: 'roo-select2-container input-group roo-select2-container-multi',
17294             cn: [
17295                 
17296                 box
17297 //                {
17298 //                    tag: 'ul',
17299 //                    cls: 'typeahead typeahead-long dropdown-menu',
17300 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17301 //                }
17302             ]
17303         };
17304         
17305         if(this.hasFeedback && !this.allowBlank){
17306             
17307             var feedback = {
17308                 tag: 'span',
17309                 cls: 'glyphicon form-control-feedback'
17310             };
17311
17312             combobox.cn.push(feedback);
17313         }
17314         
17315         
17316         
17317         var indicator = {
17318             tag : 'i',
17319             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17320             tooltip : 'This field is required'
17321         };
17322         if (Roo.bootstrap.version == 4) {
17323             indicator = {
17324                 tag : 'i',
17325                 style : 'display:none'
17326             };
17327         }
17328         if (align ==='left' && this.fieldLabel.length) {
17329             
17330             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17331             
17332             cfg.cn = [
17333                 indicator,
17334                 {
17335                     tag: 'label',
17336                     'for' :  id,
17337                     cls : 'control-label col-form-label',
17338                     html : this.fieldLabel
17339
17340                 },
17341                 {
17342                     cls : "", 
17343                     cn: [
17344                         combobox
17345                     ]
17346                 }
17347
17348             ];
17349             
17350             var labelCfg = cfg.cn[1];
17351             var contentCfg = cfg.cn[2];
17352             
17353
17354             if(this.indicatorpos == 'right'){
17355                 
17356                 cfg.cn = [
17357                     {
17358                         tag: 'label',
17359                         'for' :  id,
17360                         cls : 'control-label col-form-label',
17361                         cn : [
17362                             {
17363                                 tag : 'span',
17364                                 html : this.fieldLabel
17365                             },
17366                             indicator
17367                         ]
17368                     },
17369                     {
17370                         cls : "",
17371                         cn: [
17372                             combobox
17373                         ]
17374                     }
17375
17376                 ];
17377                 
17378                 
17379                 
17380                 labelCfg = cfg.cn[0];
17381                 contentCfg = cfg.cn[1];
17382             
17383             }
17384             
17385             if(this.labelWidth > 12){
17386                 labelCfg.style = "width: " + this.labelWidth + 'px';
17387             }
17388             if(this.width * 1 > 0){
17389                 contentCfg.style = "width: " + this.width + 'px';
17390             }
17391             if(this.labelWidth < 13 && this.labelmd == 0){
17392                 this.labelmd = this.labelWidth;
17393             }
17394             
17395             if(this.labellg > 0){
17396                 labelCfg.cls += ' col-lg-' + this.labellg;
17397                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17398             }
17399             
17400             if(this.labelmd > 0){
17401                 labelCfg.cls += ' col-md-' + this.labelmd;
17402                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17403             }
17404             
17405             if(this.labelsm > 0){
17406                 labelCfg.cls += ' col-sm-' + this.labelsm;
17407                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17408             }
17409             
17410             if(this.labelxs > 0){
17411                 labelCfg.cls += ' col-xs-' + this.labelxs;
17412                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17413             }
17414                 
17415                 
17416         } else if ( this.fieldLabel.length) {
17417 //                Roo.log(" label");
17418                  cfg.cn = [
17419                    indicator,
17420                     {
17421                         tag: 'label',
17422                         //cls : 'input-group-addon',
17423                         html : this.fieldLabel
17424                     },
17425                     combobox
17426                 ];
17427                 
17428                 if(this.indicatorpos == 'right'){
17429                     cfg.cn = [
17430                         {
17431                             tag: 'label',
17432                             //cls : 'input-group-addon',
17433                             html : this.fieldLabel
17434                         },
17435                         indicator,
17436                         combobox
17437                     ];
17438                     
17439                 }
17440
17441         } else {
17442             
17443 //                Roo.log(" no label && no align");
17444                 cfg = combobox
17445                      
17446                 
17447         }
17448          
17449         var settings=this;
17450         ['xs','sm','md','lg'].map(function(size){
17451             if (settings[size]) {
17452                 cfg.cls += ' col-' + size + '-' + settings[size];
17453             }
17454         });
17455         
17456         return cfg;
17457         
17458     },
17459     
17460     _initEventsCalled : false,
17461     
17462     // private
17463     initEvents: function()
17464     {   
17465         if (this._initEventsCalled) { // as we call render... prevent looping...
17466             return;
17467         }
17468         this._initEventsCalled = true;
17469         
17470         if (!this.store) {
17471             throw "can not find store for combo";
17472         }
17473         
17474         this.indicator = this.indicatorEl();
17475         
17476         this.store = Roo.factory(this.store, Roo.data);
17477         this.store.parent = this;
17478         
17479         // if we are building from html. then this element is so complex, that we can not really
17480         // use the rendered HTML.
17481         // so we have to trash and replace the previous code.
17482         if (Roo.XComponent.build_from_html) {
17483             // remove this element....
17484             var e = this.el.dom, k=0;
17485             while (e ) { e = e.previousSibling;  ++k;}
17486
17487             this.el.remove();
17488             
17489             this.el=false;
17490             this.rendered = false;
17491             
17492             this.render(this.parent().getChildContainer(true), k);
17493         }
17494         
17495         if(Roo.isIOS && this.useNativeIOS){
17496             this.initIOSView();
17497             return;
17498         }
17499         
17500         /*
17501          * Touch Devices
17502          */
17503         
17504         if(Roo.isTouch && this.mobileTouchView){
17505             this.initTouchView();
17506             return;
17507         }
17508         
17509         if(this.tickable){
17510             this.initTickableEvents();
17511             return;
17512         }
17513         
17514         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17515         
17516         if(this.hiddenName){
17517             
17518             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17519             
17520             this.hiddenField.dom.value =
17521                 this.hiddenValue !== undefined ? this.hiddenValue :
17522                 this.value !== undefined ? this.value : '';
17523
17524             // prevent input submission
17525             this.el.dom.removeAttribute('name');
17526             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17527              
17528              
17529         }
17530         //if(Roo.isGecko){
17531         //    this.el.dom.setAttribute('autocomplete', 'off');
17532         //}
17533         
17534         var cls = 'x-combo-list';
17535         
17536         //this.list = new Roo.Layer({
17537         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17538         //});
17539         
17540         var _this = this;
17541         
17542         (function(){
17543             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17544             _this.list.setWidth(lw);
17545         }).defer(100);
17546         
17547         this.list.on('mouseover', this.onViewOver, this);
17548         this.list.on('mousemove', this.onViewMove, this);
17549         this.list.on('scroll', this.onViewScroll, this);
17550         
17551         /*
17552         this.list.swallowEvent('mousewheel');
17553         this.assetHeight = 0;
17554
17555         if(this.title){
17556             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17557             this.assetHeight += this.header.getHeight();
17558         }
17559
17560         this.innerList = this.list.createChild({cls:cls+'-inner'});
17561         this.innerList.on('mouseover', this.onViewOver, this);
17562         this.innerList.on('mousemove', this.onViewMove, this);
17563         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17564         
17565         if(this.allowBlank && !this.pageSize && !this.disableClear){
17566             this.footer = this.list.createChild({cls:cls+'-ft'});
17567             this.pageTb = new Roo.Toolbar(this.footer);
17568            
17569         }
17570         if(this.pageSize){
17571             this.footer = this.list.createChild({cls:cls+'-ft'});
17572             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17573                     {pageSize: this.pageSize});
17574             
17575         }
17576         
17577         if (this.pageTb && this.allowBlank && !this.disableClear) {
17578             var _this = this;
17579             this.pageTb.add(new Roo.Toolbar.Fill(), {
17580                 cls: 'x-btn-icon x-btn-clear',
17581                 text: '&#160;',
17582                 handler: function()
17583                 {
17584                     _this.collapse();
17585                     _this.clearValue();
17586                     _this.onSelect(false, -1);
17587                 }
17588             });
17589         }
17590         if (this.footer) {
17591             this.assetHeight += this.footer.getHeight();
17592         }
17593         */
17594             
17595         if(!this.tpl){
17596             this.tpl = Roo.bootstrap.version == 4 ?
17597                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17598                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17599         }
17600
17601         this.view = new Roo.View(this.list, this.tpl, {
17602             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17603         });
17604         //this.view.wrapEl.setDisplayed(false);
17605         this.view.on('click', this.onViewClick, this);
17606         
17607         
17608         this.store.on('beforeload', this.onBeforeLoad, this);
17609         this.store.on('load', this.onLoad, this);
17610         this.store.on('loadexception', this.onLoadException, this);
17611         /*
17612         if(this.resizable){
17613             this.resizer = new Roo.Resizable(this.list,  {
17614                pinned:true, handles:'se'
17615             });
17616             this.resizer.on('resize', function(r, w, h){
17617                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17618                 this.listWidth = w;
17619                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17620                 this.restrictHeight();
17621             }, this);
17622             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17623         }
17624         */
17625         if(!this.editable){
17626             this.editable = true;
17627             this.setEditable(false);
17628         }
17629         
17630         /*
17631         
17632         if (typeof(this.events.add.listeners) != 'undefined') {
17633             
17634             this.addicon = this.wrap.createChild(
17635                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17636        
17637             this.addicon.on('click', function(e) {
17638                 this.fireEvent('add', this);
17639             }, this);
17640         }
17641         if (typeof(this.events.edit.listeners) != 'undefined') {
17642             
17643             this.editicon = this.wrap.createChild(
17644                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17645             if (this.addicon) {
17646                 this.editicon.setStyle('margin-left', '40px');
17647             }
17648             this.editicon.on('click', function(e) {
17649                 
17650                 // we fire even  if inothing is selected..
17651                 this.fireEvent('edit', this, this.lastData );
17652                 
17653             }, this);
17654         }
17655         */
17656         
17657         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17658             "up" : function(e){
17659                 this.inKeyMode = true;
17660                 this.selectPrev();
17661             },
17662
17663             "down" : function(e){
17664                 if(!this.isExpanded()){
17665                     this.onTriggerClick();
17666                 }else{
17667                     this.inKeyMode = true;
17668                     this.selectNext();
17669                 }
17670             },
17671
17672             "enter" : function(e){
17673 //                this.onViewClick();
17674                 //return true;
17675                 this.collapse();
17676                 
17677                 if(this.fireEvent("specialkey", this, e)){
17678                     this.onViewClick(false);
17679                 }
17680                 
17681                 return true;
17682             },
17683
17684             "esc" : function(e){
17685                 this.collapse();
17686             },
17687
17688             "tab" : function(e){
17689                 this.collapse();
17690                 
17691                 if(this.fireEvent("specialkey", this, e)){
17692                     this.onViewClick(false);
17693                 }
17694                 
17695                 return true;
17696             },
17697
17698             scope : this,
17699
17700             doRelay : function(foo, bar, hname){
17701                 if(hname == 'down' || this.scope.isExpanded()){
17702                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17703                 }
17704                 return true;
17705             },
17706
17707             forceKeyDown: true
17708         });
17709         
17710         
17711         this.queryDelay = Math.max(this.queryDelay || 10,
17712                 this.mode == 'local' ? 10 : 250);
17713         
17714         
17715         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17716         
17717         if(this.typeAhead){
17718             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17719         }
17720         if(this.editable !== false){
17721             this.inputEl().on("keyup", this.onKeyUp, this);
17722         }
17723         if(this.forceSelection){
17724             this.inputEl().on('blur', this.doForce, this);
17725         }
17726         
17727         if(this.multiple){
17728             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17729             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17730         }
17731     },
17732     
17733     initTickableEvents: function()
17734     {   
17735         this.createList();
17736         
17737         if(this.hiddenName){
17738             
17739             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17740             
17741             this.hiddenField.dom.value =
17742                 this.hiddenValue !== undefined ? this.hiddenValue :
17743                 this.value !== undefined ? this.value : '';
17744
17745             // prevent input submission
17746             this.el.dom.removeAttribute('name');
17747             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17748              
17749              
17750         }
17751         
17752 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17753         
17754         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17755         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17756         if(this.triggerList){
17757             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17758         }
17759          
17760         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17761         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17762         
17763         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17764         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17765         
17766         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17767         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17768         
17769         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17770         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17771         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17772         
17773         this.okBtn.hide();
17774         this.cancelBtn.hide();
17775         
17776         var _this = this;
17777         
17778         (function(){
17779             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17780             _this.list.setWidth(lw);
17781         }).defer(100);
17782         
17783         this.list.on('mouseover', this.onViewOver, this);
17784         this.list.on('mousemove', this.onViewMove, this);
17785         
17786         this.list.on('scroll', this.onViewScroll, this);
17787         
17788         if(!this.tpl){
17789             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17790                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17791         }
17792
17793         this.view = new Roo.View(this.list, this.tpl, {
17794             singleSelect:true,
17795             tickable:true,
17796             parent:this,
17797             store: this.store,
17798             selectedClass: this.selectedClass
17799         });
17800         
17801         //this.view.wrapEl.setDisplayed(false);
17802         this.view.on('click', this.onViewClick, this);
17803         
17804         
17805         
17806         this.store.on('beforeload', this.onBeforeLoad, this);
17807         this.store.on('load', this.onLoad, this);
17808         this.store.on('loadexception', this.onLoadException, this);
17809         
17810         if(this.editable){
17811             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17812                 "up" : function(e){
17813                     this.inKeyMode = true;
17814                     this.selectPrev();
17815                 },
17816
17817                 "down" : function(e){
17818                     this.inKeyMode = true;
17819                     this.selectNext();
17820                 },
17821
17822                 "enter" : function(e){
17823                     if(this.fireEvent("specialkey", this, e)){
17824                         this.onViewClick(false);
17825                     }
17826                     
17827                     return true;
17828                 },
17829
17830                 "esc" : function(e){
17831                     this.onTickableFooterButtonClick(e, false, false);
17832                 },
17833
17834                 "tab" : function(e){
17835                     this.fireEvent("specialkey", this, e);
17836                     
17837                     this.onTickableFooterButtonClick(e, false, false);
17838                     
17839                     return true;
17840                 },
17841
17842                 scope : this,
17843
17844                 doRelay : function(e, fn, key){
17845                     if(this.scope.isExpanded()){
17846                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17847                     }
17848                     return true;
17849                 },
17850
17851                 forceKeyDown: true
17852             });
17853         }
17854         
17855         this.queryDelay = Math.max(this.queryDelay || 10,
17856                 this.mode == 'local' ? 10 : 250);
17857         
17858         
17859         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17860         
17861         if(this.typeAhead){
17862             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17863         }
17864         
17865         if(this.editable !== false){
17866             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17867         }
17868         
17869         this.indicator = this.indicatorEl();
17870         
17871         if(this.indicator){
17872             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17873             this.indicator.hide();
17874         }
17875         
17876     },
17877
17878     onDestroy : function(){
17879         if(this.view){
17880             this.view.setStore(null);
17881             this.view.el.removeAllListeners();
17882             this.view.el.remove();
17883             this.view.purgeListeners();
17884         }
17885         if(this.list){
17886             this.list.dom.innerHTML  = '';
17887         }
17888         
17889         if(this.store){
17890             this.store.un('beforeload', this.onBeforeLoad, this);
17891             this.store.un('load', this.onLoad, this);
17892             this.store.un('loadexception', this.onLoadException, this);
17893         }
17894         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17895     },
17896
17897     // private
17898     fireKey : function(e){
17899         if(e.isNavKeyPress() && !this.list.isVisible()){
17900             this.fireEvent("specialkey", this, e);
17901         }
17902     },
17903
17904     // private
17905     onResize: function(w, h)
17906     {
17907         
17908         
17909 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17910 //        
17911 //        if(typeof w != 'number'){
17912 //            // we do not handle it!?!?
17913 //            return;
17914 //        }
17915 //        var tw = this.trigger.getWidth();
17916 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17917 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17918 //        var x = w - tw;
17919 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17920 //            
17921 //        //this.trigger.setStyle('left', x+'px');
17922 //        
17923 //        if(this.list && this.listWidth === undefined){
17924 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17925 //            this.list.setWidth(lw);
17926 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17927 //        }
17928         
17929     
17930         
17931     },
17932
17933     /**
17934      * Allow or prevent the user from directly editing the field text.  If false is passed,
17935      * the user will only be able to select from the items defined in the dropdown list.  This method
17936      * is the runtime equivalent of setting the 'editable' config option at config time.
17937      * @param {Boolean} value True to allow the user to directly edit the field text
17938      */
17939     setEditable : function(value){
17940         if(value == this.editable){
17941             return;
17942         }
17943         this.editable = value;
17944         if(!value){
17945             this.inputEl().dom.setAttribute('readOnly', true);
17946             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17947             this.inputEl().addClass('x-combo-noedit');
17948         }else{
17949             this.inputEl().dom.removeAttribute('readOnly');
17950             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17951             this.inputEl().removeClass('x-combo-noedit');
17952         }
17953     },
17954
17955     // private
17956     
17957     onBeforeLoad : function(combo,opts){
17958         if(!this.hasFocus){
17959             return;
17960         }
17961          if (!opts.add) {
17962             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17963          }
17964         this.restrictHeight();
17965         this.selectedIndex = -1;
17966     },
17967
17968     // private
17969     onLoad : function(){
17970         
17971         this.hasQuery = false;
17972         
17973         if(!this.hasFocus){
17974             return;
17975         }
17976         
17977         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17978             this.loading.hide();
17979         }
17980         
17981         if(this.store.getCount() > 0){
17982             
17983             this.expand();
17984             this.restrictHeight();
17985             if(this.lastQuery == this.allQuery){
17986                 if(this.editable && !this.tickable){
17987                     this.inputEl().dom.select();
17988                 }
17989                 
17990                 if(
17991                     !this.selectByValue(this.value, true) &&
17992                     this.autoFocus && 
17993                     (
17994                         !this.store.lastOptions ||
17995                         typeof(this.store.lastOptions.add) == 'undefined' || 
17996                         this.store.lastOptions.add != true
17997                     )
17998                 ){
17999                     this.select(0, true);
18000                 }
18001             }else{
18002                 if(this.autoFocus){
18003                     this.selectNext();
18004                 }
18005                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18006                     this.taTask.delay(this.typeAheadDelay);
18007                 }
18008             }
18009         }else{
18010             this.onEmptyResults();
18011         }
18012         
18013         //this.el.focus();
18014     },
18015     // private
18016     onLoadException : function()
18017     {
18018         this.hasQuery = false;
18019         
18020         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18021             this.loading.hide();
18022         }
18023         
18024         if(this.tickable && this.editable){
18025             return;
18026         }
18027         
18028         this.collapse();
18029         // only causes errors at present
18030         //Roo.log(this.store.reader.jsonData);
18031         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18032             // fixme
18033             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18034         //}
18035         
18036         
18037     },
18038     // private
18039     onTypeAhead : function(){
18040         if(this.store.getCount() > 0){
18041             var r = this.store.getAt(0);
18042             var newValue = r.data[this.displayField];
18043             var len = newValue.length;
18044             var selStart = this.getRawValue().length;
18045             
18046             if(selStart != len){
18047                 this.setRawValue(newValue);
18048                 this.selectText(selStart, newValue.length);
18049             }
18050         }
18051     },
18052
18053     // private
18054     onSelect : function(record, index){
18055         
18056         if(this.fireEvent('beforeselect', this, record, index) !== false){
18057         
18058             this.setFromData(index > -1 ? record.data : false);
18059             
18060             this.collapse();
18061             this.fireEvent('select', this, record, index);
18062         }
18063     },
18064
18065     /**
18066      * Returns the currently selected field value or empty string if no value is set.
18067      * @return {String} value The selected value
18068      */
18069     getValue : function()
18070     {
18071         if(Roo.isIOS && this.useNativeIOS){
18072             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18073         }
18074         
18075         if(this.multiple){
18076             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18077         }
18078         
18079         if(this.valueField){
18080             return typeof this.value != 'undefined' ? this.value : '';
18081         }else{
18082             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18083         }
18084     },
18085     
18086     getRawValue : function()
18087     {
18088         if(Roo.isIOS && this.useNativeIOS){
18089             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18090         }
18091         
18092         var v = this.inputEl().getValue();
18093         
18094         return v;
18095     },
18096
18097     /**
18098      * Clears any text/value currently set in the field
18099      */
18100     clearValue : function(){
18101         
18102         if(this.hiddenField){
18103             this.hiddenField.dom.value = '';
18104         }
18105         this.value = '';
18106         this.setRawValue('');
18107         this.lastSelectionText = '';
18108         this.lastData = false;
18109         
18110         var close = this.closeTriggerEl();
18111         
18112         if(close){
18113             close.hide();
18114         }
18115         
18116         this.validate();
18117         
18118     },
18119
18120     /**
18121      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18122      * will be displayed in the field.  If the value does not match the data value of an existing item,
18123      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18124      * Otherwise the field will be blank (although the value will still be set).
18125      * @param {String} value The value to match
18126      */
18127     setValue : function(v)
18128     {
18129         if(Roo.isIOS && this.useNativeIOS){
18130             this.setIOSValue(v);
18131             return;
18132         }
18133         
18134         if(this.multiple){
18135             this.syncValue();
18136             return;
18137         }
18138         
18139         var text = v;
18140         if(this.valueField){
18141             var r = this.findRecord(this.valueField, v);
18142             if(r){
18143                 text = r.data[this.displayField];
18144             }else if(this.valueNotFoundText !== undefined){
18145                 text = this.valueNotFoundText;
18146             }
18147         }
18148         this.lastSelectionText = text;
18149         if(this.hiddenField){
18150             this.hiddenField.dom.value = v;
18151         }
18152         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18153         this.value = v;
18154         
18155         var close = this.closeTriggerEl();
18156         
18157         if(close){
18158             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18159         }
18160         
18161         this.validate();
18162     },
18163     /**
18164      * @property {Object} the last set data for the element
18165      */
18166     
18167     lastData : false,
18168     /**
18169      * Sets the value of the field based on a object which is related to the record format for the store.
18170      * @param {Object} value the value to set as. or false on reset?
18171      */
18172     setFromData : function(o){
18173         
18174         if(this.multiple){
18175             this.addItem(o);
18176             return;
18177         }
18178             
18179         var dv = ''; // display value
18180         var vv = ''; // value value..
18181         this.lastData = o;
18182         if (this.displayField) {
18183             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18184         } else {
18185             // this is an error condition!!!
18186             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18187         }
18188         
18189         if(this.valueField){
18190             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18191         }
18192         
18193         var close = this.closeTriggerEl();
18194         
18195         if(close){
18196             if(dv.length || vv * 1 > 0){
18197                 close.show() ;
18198                 this.blockFocus=true;
18199             } else {
18200                 close.hide();
18201             }             
18202         }
18203         
18204         if(this.hiddenField){
18205             this.hiddenField.dom.value = vv;
18206             
18207             this.lastSelectionText = dv;
18208             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18209             this.value = vv;
18210             return;
18211         }
18212         // no hidden field.. - we store the value in 'value', but still display
18213         // display field!!!!
18214         this.lastSelectionText = dv;
18215         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18216         this.value = vv;
18217         
18218         
18219         
18220     },
18221     // private
18222     reset : function(){
18223         // overridden so that last data is reset..
18224         
18225         if(this.multiple){
18226             this.clearItem();
18227             return;
18228         }
18229         
18230         this.setValue(this.originalValue);
18231         //this.clearInvalid();
18232         this.lastData = false;
18233         if (this.view) {
18234             this.view.clearSelections();
18235         }
18236         
18237         this.validate();
18238     },
18239     // private
18240     findRecord : function(prop, value){
18241         var record;
18242         if(this.store.getCount() > 0){
18243             this.store.each(function(r){
18244                 if(r.data[prop] == value){
18245                     record = r;
18246                     return false;
18247                 }
18248                 return true;
18249             });
18250         }
18251         return record;
18252     },
18253     
18254     getName: function()
18255     {
18256         // returns hidden if it's set..
18257         if (!this.rendered) {return ''};
18258         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18259         
18260     },
18261     // private
18262     onViewMove : function(e, t){
18263         this.inKeyMode = false;
18264     },
18265
18266     // private
18267     onViewOver : function(e, t){
18268         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18269             return;
18270         }
18271         var item = this.view.findItemFromChild(t);
18272         
18273         if(item){
18274             var index = this.view.indexOf(item);
18275             this.select(index, false);
18276         }
18277     },
18278
18279     // private
18280     onViewClick : function(view, doFocus, el, e)
18281     {
18282         var index = this.view.getSelectedIndexes()[0];
18283         
18284         var r = this.store.getAt(index);
18285         
18286         if(this.tickable){
18287             
18288             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18289                 return;
18290             }
18291             
18292             var rm = false;
18293             var _this = this;
18294             
18295             Roo.each(this.tickItems, function(v,k){
18296                 
18297                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18298                     Roo.log(v);
18299                     _this.tickItems.splice(k, 1);
18300                     
18301                     if(typeof(e) == 'undefined' && view == false){
18302                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18303                     }
18304                     
18305                     rm = true;
18306                     return;
18307                 }
18308             });
18309             
18310             if(rm){
18311                 return;
18312             }
18313             
18314             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18315                 this.tickItems.push(r.data);
18316             }
18317             
18318             if(typeof(e) == 'undefined' && view == false){
18319                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18320             }
18321                     
18322             return;
18323         }
18324         
18325         if(r){
18326             this.onSelect(r, index);
18327         }
18328         if(doFocus !== false && !this.blockFocus){
18329             this.inputEl().focus();
18330         }
18331     },
18332
18333     // private
18334     restrictHeight : function(){
18335         //this.innerList.dom.style.height = '';
18336         //var inner = this.innerList.dom;
18337         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18338         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18339         //this.list.beginUpdate();
18340         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18341         this.list.alignTo(this.inputEl(), this.listAlign);
18342         this.list.alignTo(this.inputEl(), this.listAlign);
18343         //this.list.endUpdate();
18344     },
18345
18346     // private
18347     onEmptyResults : function(){
18348         
18349         if(this.tickable && this.editable){
18350             this.hasFocus = false;
18351             this.restrictHeight();
18352             return;
18353         }
18354         
18355         this.collapse();
18356     },
18357
18358     /**
18359      * Returns true if the dropdown list is expanded, else false.
18360      */
18361     isExpanded : function(){
18362         return this.list.isVisible();
18363     },
18364
18365     /**
18366      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18367      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18368      * @param {String} value The data value of the item to select
18369      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18370      * selected item if it is not currently in view (defaults to true)
18371      * @return {Boolean} True if the value matched an item in the list, else false
18372      */
18373     selectByValue : function(v, scrollIntoView){
18374         if(v !== undefined && v !== null){
18375             var r = this.findRecord(this.valueField || this.displayField, v);
18376             if(r){
18377                 this.select(this.store.indexOf(r), scrollIntoView);
18378                 return true;
18379             }
18380         }
18381         return false;
18382     },
18383
18384     /**
18385      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18386      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18387      * @param {Number} index The zero-based index of the list item to select
18388      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18389      * selected item if it is not currently in view (defaults to true)
18390      */
18391     select : function(index, scrollIntoView){
18392         this.selectedIndex = index;
18393         this.view.select(index);
18394         if(scrollIntoView !== false){
18395             var el = this.view.getNode(index);
18396             /*
18397              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18398              */
18399             if(el){
18400                 this.list.scrollChildIntoView(el, false);
18401             }
18402         }
18403     },
18404
18405     // private
18406     selectNext : function(){
18407         var ct = this.store.getCount();
18408         if(ct > 0){
18409             if(this.selectedIndex == -1){
18410                 this.select(0);
18411             }else if(this.selectedIndex < ct-1){
18412                 this.select(this.selectedIndex+1);
18413             }
18414         }
18415     },
18416
18417     // private
18418     selectPrev : function(){
18419         var ct = this.store.getCount();
18420         if(ct > 0){
18421             if(this.selectedIndex == -1){
18422                 this.select(0);
18423             }else if(this.selectedIndex != 0){
18424                 this.select(this.selectedIndex-1);
18425             }
18426         }
18427     },
18428
18429     // private
18430     onKeyUp : function(e){
18431         if(this.editable !== false && !e.isSpecialKey()){
18432             this.lastKey = e.getKey();
18433             this.dqTask.delay(this.queryDelay);
18434         }
18435     },
18436
18437     // private
18438     validateBlur : function(){
18439         return !this.list || !this.list.isVisible();   
18440     },
18441
18442     // private
18443     initQuery : function(){
18444         
18445         var v = this.getRawValue();
18446         
18447         if(this.tickable && this.editable){
18448             v = this.tickableInputEl().getValue();
18449         }
18450         
18451         this.doQuery(v);
18452     },
18453
18454     // private
18455     doForce : function(){
18456         if(this.inputEl().dom.value.length > 0){
18457             this.inputEl().dom.value =
18458                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18459              
18460         }
18461     },
18462
18463     /**
18464      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18465      * query allowing the query action to be canceled if needed.
18466      * @param {String} query The SQL query to execute
18467      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18468      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18469      * saved in the current store (defaults to false)
18470      */
18471     doQuery : function(q, forceAll){
18472         
18473         if(q === undefined || q === null){
18474             q = '';
18475         }
18476         var qe = {
18477             query: q,
18478             forceAll: forceAll,
18479             combo: this,
18480             cancel:false
18481         };
18482         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18483             return false;
18484         }
18485         q = qe.query;
18486         
18487         forceAll = qe.forceAll;
18488         if(forceAll === true || (q.length >= this.minChars)){
18489             
18490             this.hasQuery = true;
18491             
18492             if(this.lastQuery != q || this.alwaysQuery){
18493                 this.lastQuery = q;
18494                 if(this.mode == 'local'){
18495                     this.selectedIndex = -1;
18496                     if(forceAll){
18497                         this.store.clearFilter();
18498                     }else{
18499                         
18500                         if(this.specialFilter){
18501                             this.fireEvent('specialfilter', this);
18502                             this.onLoad();
18503                             return;
18504                         }
18505                         
18506                         this.store.filter(this.displayField, q);
18507                     }
18508                     
18509                     this.store.fireEvent("datachanged", this.store);
18510                     
18511                     this.onLoad();
18512                     
18513                     
18514                 }else{
18515                     
18516                     this.store.baseParams[this.queryParam] = q;
18517                     
18518                     var options = {params : this.getParams(q)};
18519                     
18520                     if(this.loadNext){
18521                         options.add = true;
18522                         options.params.start = this.page * this.pageSize;
18523                     }
18524                     
18525                     this.store.load(options);
18526                     
18527                     /*
18528                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18529                      *  we should expand the list on onLoad
18530                      *  so command out it
18531                      */
18532 //                    this.expand();
18533                 }
18534             }else{
18535                 this.selectedIndex = -1;
18536                 this.onLoad();   
18537             }
18538         }
18539         
18540         this.loadNext = false;
18541     },
18542     
18543     // private
18544     getParams : function(q){
18545         var p = {};
18546         //p[this.queryParam] = q;
18547         
18548         if(this.pageSize){
18549             p.start = 0;
18550             p.limit = this.pageSize;
18551         }
18552         return p;
18553     },
18554
18555     /**
18556      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18557      */
18558     collapse : function(){
18559         if(!this.isExpanded()){
18560             return;
18561         }
18562         
18563         this.list.hide();
18564         
18565         this.hasFocus = false;
18566         
18567         if(this.tickable){
18568             this.okBtn.hide();
18569             this.cancelBtn.hide();
18570             this.trigger.show();
18571             
18572             if(this.editable){
18573                 this.tickableInputEl().dom.value = '';
18574                 this.tickableInputEl().blur();
18575             }
18576             
18577         }
18578         
18579         Roo.get(document).un('mousedown', this.collapseIf, this);
18580         Roo.get(document).un('mousewheel', this.collapseIf, this);
18581         if (!this.editable) {
18582             Roo.get(document).un('keydown', this.listKeyPress, this);
18583         }
18584         this.fireEvent('collapse', this);
18585         
18586         this.validate();
18587     },
18588
18589     // private
18590     collapseIf : function(e){
18591         var in_combo  = e.within(this.el);
18592         var in_list =  e.within(this.list);
18593         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18594         
18595         if (in_combo || in_list || is_list) {
18596             //e.stopPropagation();
18597             return;
18598         }
18599         
18600         if(this.tickable){
18601             this.onTickableFooterButtonClick(e, false, false);
18602         }
18603
18604         this.collapse();
18605         
18606     },
18607
18608     /**
18609      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18610      */
18611     expand : function(){
18612        
18613         if(this.isExpanded() || !this.hasFocus){
18614             return;
18615         }
18616         
18617         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18618         this.list.setWidth(lw);
18619         
18620         Roo.log('expand');
18621         
18622         this.list.show();
18623         
18624         this.restrictHeight();
18625         
18626         if(this.tickable){
18627             
18628             this.tickItems = Roo.apply([], this.item);
18629             
18630             this.okBtn.show();
18631             this.cancelBtn.show();
18632             this.trigger.hide();
18633             
18634             if(this.editable){
18635                 this.tickableInputEl().focus();
18636             }
18637             
18638         }
18639         
18640         Roo.get(document).on('mousedown', this.collapseIf, this);
18641         Roo.get(document).on('mousewheel', this.collapseIf, this);
18642         if (!this.editable) {
18643             Roo.get(document).on('keydown', this.listKeyPress, this);
18644         }
18645         
18646         this.fireEvent('expand', this);
18647     },
18648
18649     // private
18650     // Implements the default empty TriggerField.onTriggerClick function
18651     onTriggerClick : function(e)
18652     {
18653         Roo.log('trigger click');
18654         
18655         if(this.disabled || !this.triggerList){
18656             return;
18657         }
18658         
18659         this.page = 0;
18660         this.loadNext = false;
18661         
18662         if(this.isExpanded()){
18663             this.collapse();
18664             if (!this.blockFocus) {
18665                 this.inputEl().focus();
18666             }
18667             
18668         }else {
18669             this.hasFocus = true;
18670             if(this.triggerAction == 'all') {
18671                 this.doQuery(this.allQuery, true);
18672             } else {
18673                 this.doQuery(this.getRawValue());
18674             }
18675             if (!this.blockFocus) {
18676                 this.inputEl().focus();
18677             }
18678         }
18679     },
18680     
18681     onTickableTriggerClick : function(e)
18682     {
18683         if(this.disabled){
18684             return;
18685         }
18686         
18687         this.page = 0;
18688         this.loadNext = false;
18689         this.hasFocus = true;
18690         
18691         if(this.triggerAction == 'all') {
18692             this.doQuery(this.allQuery, true);
18693         } else {
18694             this.doQuery(this.getRawValue());
18695         }
18696     },
18697     
18698     onSearchFieldClick : function(e)
18699     {
18700         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18701             this.onTickableFooterButtonClick(e, false, false);
18702             return;
18703         }
18704         
18705         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18706             return;
18707         }
18708         
18709         this.page = 0;
18710         this.loadNext = false;
18711         this.hasFocus = true;
18712         
18713         if(this.triggerAction == 'all') {
18714             this.doQuery(this.allQuery, true);
18715         } else {
18716             this.doQuery(this.getRawValue());
18717         }
18718     },
18719     
18720     listKeyPress : function(e)
18721     {
18722         //Roo.log('listkeypress');
18723         // scroll to first matching element based on key pres..
18724         if (e.isSpecialKey()) {
18725             return false;
18726         }
18727         var k = String.fromCharCode(e.getKey()).toUpperCase();
18728         //Roo.log(k);
18729         var match  = false;
18730         var csel = this.view.getSelectedNodes();
18731         var cselitem = false;
18732         if (csel.length) {
18733             var ix = this.view.indexOf(csel[0]);
18734             cselitem  = this.store.getAt(ix);
18735             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18736                 cselitem = false;
18737             }
18738             
18739         }
18740         
18741         this.store.each(function(v) { 
18742             if (cselitem) {
18743                 // start at existing selection.
18744                 if (cselitem.id == v.id) {
18745                     cselitem = false;
18746                 }
18747                 return true;
18748             }
18749                 
18750             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18751                 match = this.store.indexOf(v);
18752                 return false;
18753             }
18754             return true;
18755         }, this);
18756         
18757         if (match === false) {
18758             return true; // no more action?
18759         }
18760         // scroll to?
18761         this.view.select(match);
18762         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18763         sn.scrollIntoView(sn.dom.parentNode, false);
18764     },
18765     
18766     onViewScroll : function(e, t){
18767         
18768         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){
18769             return;
18770         }
18771         
18772         this.hasQuery = true;
18773         
18774         this.loading = this.list.select('.loading', true).first();
18775         
18776         if(this.loading === null){
18777             this.list.createChild({
18778                 tag: 'div',
18779                 cls: 'loading roo-select2-more-results roo-select2-active',
18780                 html: 'Loading more results...'
18781             });
18782             
18783             this.loading = this.list.select('.loading', true).first();
18784             
18785             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18786             
18787             this.loading.hide();
18788         }
18789         
18790         this.loading.show();
18791         
18792         var _combo = this;
18793         
18794         this.page++;
18795         this.loadNext = true;
18796         
18797         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18798         
18799         return;
18800     },
18801     
18802     addItem : function(o)
18803     {   
18804         var dv = ''; // display value
18805         
18806         if (this.displayField) {
18807             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18808         } else {
18809             // this is an error condition!!!
18810             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18811         }
18812         
18813         if(!dv.length){
18814             return;
18815         }
18816         
18817         var choice = this.choices.createChild({
18818             tag: 'li',
18819             cls: 'roo-select2-search-choice',
18820             cn: [
18821                 {
18822                     tag: 'div',
18823                     html: dv
18824                 },
18825                 {
18826                     tag: 'a',
18827                     href: '#',
18828                     cls: 'roo-select2-search-choice-close fa fa-times',
18829                     tabindex: '-1'
18830                 }
18831             ]
18832             
18833         }, this.searchField);
18834         
18835         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18836         
18837         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18838         
18839         this.item.push(o);
18840         
18841         this.lastData = o;
18842         
18843         this.syncValue();
18844         
18845         this.inputEl().dom.value = '';
18846         
18847         this.validate();
18848     },
18849     
18850     onRemoveItem : function(e, _self, o)
18851     {
18852         e.preventDefault();
18853         
18854         this.lastItem = Roo.apply([], this.item);
18855         
18856         var index = this.item.indexOf(o.data) * 1;
18857         
18858         if( index < 0){
18859             Roo.log('not this item?!');
18860             return;
18861         }
18862         
18863         this.item.splice(index, 1);
18864         o.item.remove();
18865         
18866         this.syncValue();
18867         
18868         this.fireEvent('remove', this, e);
18869         
18870         this.validate();
18871         
18872     },
18873     
18874     syncValue : function()
18875     {
18876         if(!this.item.length){
18877             this.clearValue();
18878             return;
18879         }
18880             
18881         var value = [];
18882         var _this = this;
18883         Roo.each(this.item, function(i){
18884             if(_this.valueField){
18885                 value.push(i[_this.valueField]);
18886                 return;
18887             }
18888
18889             value.push(i);
18890         });
18891
18892         this.value = value.join(',');
18893
18894         if(this.hiddenField){
18895             this.hiddenField.dom.value = this.value;
18896         }
18897         
18898         this.store.fireEvent("datachanged", this.store);
18899         
18900         this.validate();
18901     },
18902     
18903     clearItem : function()
18904     {
18905         if(!this.multiple){
18906             return;
18907         }
18908         
18909         this.item = [];
18910         
18911         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18912            c.remove();
18913         });
18914         
18915         this.syncValue();
18916         
18917         this.validate();
18918         
18919         if(this.tickable && !Roo.isTouch){
18920             this.view.refresh();
18921         }
18922     },
18923     
18924     inputEl: function ()
18925     {
18926         if(Roo.isIOS && this.useNativeIOS){
18927             return this.el.select('select.roo-ios-select', true).first();
18928         }
18929         
18930         if(Roo.isTouch && this.mobileTouchView){
18931             return this.el.select('input.form-control',true).first();
18932         }
18933         
18934         if(this.tickable){
18935             return this.searchField;
18936         }
18937         
18938         return this.el.select('input.form-control',true).first();
18939     },
18940     
18941     onTickableFooterButtonClick : function(e, btn, el)
18942     {
18943         e.preventDefault();
18944         
18945         this.lastItem = Roo.apply([], this.item);
18946         
18947         if(btn && btn.name == 'cancel'){
18948             this.tickItems = Roo.apply([], this.item);
18949             this.collapse();
18950             return;
18951         }
18952         
18953         this.clearItem();
18954         
18955         var _this = this;
18956         
18957         Roo.each(this.tickItems, function(o){
18958             _this.addItem(o);
18959         });
18960         
18961         this.collapse();
18962         
18963     },
18964     
18965     validate : function()
18966     {
18967         if(this.getVisibilityEl().hasClass('hidden')){
18968             return true;
18969         }
18970         
18971         var v = this.getRawValue();
18972         
18973         if(this.multiple){
18974             v = this.getValue();
18975         }
18976         
18977         if(this.disabled || this.allowBlank || v.length){
18978             this.markValid();
18979             return true;
18980         }
18981         
18982         this.markInvalid();
18983         return false;
18984     },
18985     
18986     tickableInputEl : function()
18987     {
18988         if(!this.tickable || !this.editable){
18989             return this.inputEl();
18990         }
18991         
18992         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18993     },
18994     
18995     
18996     getAutoCreateTouchView : function()
18997     {
18998         var id = Roo.id();
18999         
19000         var cfg = {
19001             cls: 'form-group' //input-group
19002         };
19003         
19004         var input =  {
19005             tag: 'input',
19006             id : id,
19007             type : this.inputType,
19008             cls : 'form-control x-combo-noedit',
19009             autocomplete: 'new-password',
19010             placeholder : this.placeholder || '',
19011             readonly : true
19012         };
19013         
19014         if (this.name) {
19015             input.name = this.name;
19016         }
19017         
19018         if (this.size) {
19019             input.cls += ' input-' + this.size;
19020         }
19021         
19022         if (this.disabled) {
19023             input.disabled = true;
19024         }
19025         
19026         var inputblock = {
19027             cls : 'roo-combobox-wrap',
19028             cn : [
19029                 input
19030             ]
19031         };
19032         
19033         if(this.before){
19034             inputblock.cls += ' input-group';
19035             
19036             inputblock.cn.unshift({
19037                 tag :'span',
19038                 cls : 'input-group-addon input-group-prepend input-group-text',
19039                 html : this.before
19040             });
19041         }
19042         
19043         if(this.removable && !this.multiple){
19044             inputblock.cls += ' roo-removable';
19045             
19046             inputblock.cn.push({
19047                 tag: 'button',
19048                 html : 'x',
19049                 cls : 'roo-combo-removable-btn close'
19050             });
19051         }
19052
19053         if(this.hasFeedback && !this.allowBlank){
19054             
19055             inputblock.cls += ' has-feedback';
19056             
19057             inputblock.cn.push({
19058                 tag: 'span',
19059                 cls: 'glyphicon form-control-feedback'
19060             });
19061             
19062         }
19063         
19064         if (this.after) {
19065             
19066             inputblock.cls += (this.before) ? '' : ' input-group';
19067             
19068             inputblock.cn.push({
19069                 tag :'span',
19070                 cls : 'input-group-addon input-group-append input-group-text',
19071                 html : this.after
19072             });
19073         }
19074
19075         
19076         var ibwrap = inputblock;
19077         
19078         if(this.multiple){
19079             ibwrap = {
19080                 tag: 'ul',
19081                 cls: 'roo-select2-choices',
19082                 cn:[
19083                     {
19084                         tag: 'li',
19085                         cls: 'roo-select2-search-field',
19086                         cn: [
19087
19088                             inputblock
19089                         ]
19090                     }
19091                 ]
19092             };
19093         
19094             
19095         }
19096         
19097         var combobox = {
19098             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19099             cn: [
19100                 {
19101                     tag: 'input',
19102                     type : 'hidden',
19103                     cls: 'form-hidden-field'
19104                 },
19105                 ibwrap
19106             ]
19107         };
19108         
19109         if(!this.multiple && this.showToggleBtn){
19110             
19111             var caret = {
19112                 cls: 'caret'
19113             };
19114             
19115             if (this.caret != false) {
19116                 caret = {
19117                      tag: 'i',
19118                      cls: 'fa fa-' + this.caret
19119                 };
19120                 
19121             }
19122             
19123             combobox.cn.push({
19124                 tag :'span',
19125                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19126                 cn : [
19127                     Roo.bootstrap.version == 3 ? caret : '',
19128                     {
19129                         tag: 'span',
19130                         cls: 'combobox-clear',
19131                         cn  : [
19132                             {
19133                                 tag : 'i',
19134                                 cls: 'icon-remove'
19135                             }
19136                         ]
19137                     }
19138                 ]
19139
19140             })
19141         }
19142         
19143         if(this.multiple){
19144             combobox.cls += ' roo-select2-container-multi';
19145         }
19146         
19147         var required =  this.allowBlank ?  {
19148                     tag : 'i',
19149                     style: 'display: none'
19150                 } : {
19151                    tag : 'i',
19152                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19153                    tooltip : 'This field is required'
19154                 };
19155         
19156         var align = this.labelAlign || this.parentLabelAlign();
19157         
19158         if (align ==='left' && this.fieldLabel.length) {
19159
19160             cfg.cn = [
19161                 required,
19162                 {
19163                     tag: 'label',
19164                     cls : 'control-label col-form-label',
19165                     html : this.fieldLabel
19166
19167                 },
19168                 {
19169                     cls : 'roo-combobox-wrap ', 
19170                     cn: [
19171                         combobox
19172                     ]
19173                 }
19174             ];
19175             
19176             var labelCfg = cfg.cn[1];
19177             var contentCfg = cfg.cn[2];
19178             
19179
19180             if(this.indicatorpos == 'right'){
19181                 cfg.cn = [
19182                     {
19183                         tag: 'label',
19184                         'for' :  id,
19185                         cls : 'control-label col-form-label',
19186                         cn : [
19187                             {
19188                                 tag : 'span',
19189                                 html : this.fieldLabel
19190                             },
19191                             required
19192                         ]
19193                     },
19194                     {
19195                         cls : "roo-combobox-wrap ",
19196                         cn: [
19197                             combobox
19198                         ]
19199                     }
19200
19201                 ];
19202                 
19203                 labelCfg = cfg.cn[0];
19204                 contentCfg = cfg.cn[1];
19205             }
19206             
19207            
19208             
19209             if(this.labelWidth > 12){
19210                 labelCfg.style = "width: " + this.labelWidth + 'px';
19211             }
19212            
19213             if(this.labelWidth < 13 && this.labelmd == 0){
19214                 this.labelmd = this.labelWidth;
19215             }
19216             
19217             if(this.labellg > 0){
19218                 labelCfg.cls += ' col-lg-' + this.labellg;
19219                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19220             }
19221             
19222             if(this.labelmd > 0){
19223                 labelCfg.cls += ' col-md-' + this.labelmd;
19224                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19225             }
19226             
19227             if(this.labelsm > 0){
19228                 labelCfg.cls += ' col-sm-' + this.labelsm;
19229                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19230             }
19231             
19232             if(this.labelxs > 0){
19233                 labelCfg.cls += ' col-xs-' + this.labelxs;
19234                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19235             }
19236                 
19237                 
19238         } else if ( this.fieldLabel.length) {
19239             cfg.cn = [
19240                required,
19241                 {
19242                     tag: 'label',
19243                     cls : 'control-label',
19244                     html : this.fieldLabel
19245
19246                 },
19247                 {
19248                     cls : '', 
19249                     cn: [
19250                         combobox
19251                     ]
19252                 }
19253             ];
19254             
19255             if(this.indicatorpos == 'right'){
19256                 cfg.cn = [
19257                     {
19258                         tag: 'label',
19259                         cls : 'control-label',
19260                         html : this.fieldLabel,
19261                         cn : [
19262                             required
19263                         ]
19264                     },
19265                     {
19266                         cls : '', 
19267                         cn: [
19268                             combobox
19269                         ]
19270                     }
19271                 ];
19272             }
19273         } else {
19274             cfg.cn = combobox;    
19275         }
19276         
19277         
19278         var settings = this;
19279         
19280         ['xs','sm','md','lg'].map(function(size){
19281             if (settings[size]) {
19282                 cfg.cls += ' col-' + size + '-' + settings[size];
19283             }
19284         });
19285         
19286         return cfg;
19287     },
19288     
19289     initTouchView : function()
19290     {
19291         this.renderTouchView();
19292         
19293         this.touchViewEl.on('scroll', function(){
19294             this.el.dom.scrollTop = 0;
19295         }, this);
19296         
19297         this.originalValue = this.getValue();
19298         
19299         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19300         
19301         this.inputEl().on("click", this.showTouchView, this);
19302         if (this.triggerEl) {
19303             this.triggerEl.on("click", this.showTouchView, this);
19304         }
19305         
19306         
19307         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19308         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19309         
19310         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19311         
19312         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19313         this.store.on('load', this.onTouchViewLoad, this);
19314         this.store.on('loadexception', this.onTouchViewLoadException, this);
19315         
19316         if(this.hiddenName){
19317             
19318             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19319             
19320             this.hiddenField.dom.value =
19321                 this.hiddenValue !== undefined ? this.hiddenValue :
19322                 this.value !== undefined ? this.value : '';
19323         
19324             this.el.dom.removeAttribute('name');
19325             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19326         }
19327         
19328         if(this.multiple){
19329             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19330             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19331         }
19332         
19333         if(this.removable && !this.multiple){
19334             var close = this.closeTriggerEl();
19335             if(close){
19336                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19337                 close.on('click', this.removeBtnClick, this, close);
19338             }
19339         }
19340         /*
19341          * fix the bug in Safari iOS8
19342          */
19343         this.inputEl().on("focus", function(e){
19344             document.activeElement.blur();
19345         }, this);
19346         
19347         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19348         
19349         return;
19350         
19351         
19352     },
19353     
19354     renderTouchView : function()
19355     {
19356         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19357         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19358         
19359         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19360         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19361         
19362         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19363         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19364         this.touchViewBodyEl.setStyle('overflow', 'auto');
19365         
19366         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19367         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19368         
19369         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19370         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19371         
19372     },
19373     
19374     showTouchView : function()
19375     {
19376         if(this.disabled){
19377             return;
19378         }
19379         
19380         this.touchViewHeaderEl.hide();
19381
19382         if(this.modalTitle.length){
19383             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19384             this.touchViewHeaderEl.show();
19385         }
19386
19387         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19388         this.touchViewEl.show();
19389
19390         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19391         
19392         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19393         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19394
19395         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19396
19397         if(this.modalTitle.length){
19398             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19399         }
19400         
19401         this.touchViewBodyEl.setHeight(bodyHeight);
19402
19403         if(this.animate){
19404             var _this = this;
19405             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19406         }else{
19407             this.touchViewEl.addClass(['in','show']);
19408         }
19409         
19410         if(this._touchViewMask){
19411             Roo.get(document.body).addClass("x-body-masked");
19412             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19413             this._touchViewMask.setStyle('z-index', 10000);
19414             this._touchViewMask.addClass('show');
19415         }
19416         
19417         this.doTouchViewQuery();
19418         
19419     },
19420     
19421     hideTouchView : function()
19422     {
19423         this.touchViewEl.removeClass(['in','show']);
19424
19425         if(this.animate){
19426             var _this = this;
19427             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19428         }else{
19429             this.touchViewEl.setStyle('display', 'none');
19430         }
19431         
19432         if(this._touchViewMask){
19433             this._touchViewMask.removeClass('show');
19434             Roo.get(document.body).removeClass("x-body-masked");
19435         }
19436     },
19437     
19438     setTouchViewValue : function()
19439     {
19440         if(this.multiple){
19441             this.clearItem();
19442         
19443             var _this = this;
19444
19445             Roo.each(this.tickItems, function(o){
19446                 this.addItem(o);
19447             }, this);
19448         }
19449         
19450         this.hideTouchView();
19451     },
19452     
19453     doTouchViewQuery : function()
19454     {
19455         var qe = {
19456             query: '',
19457             forceAll: true,
19458             combo: this,
19459             cancel:false
19460         };
19461         
19462         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19463             return false;
19464         }
19465         
19466         if(!this.alwaysQuery || this.mode == 'local'){
19467             this.onTouchViewLoad();
19468             return;
19469         }
19470         
19471         this.store.load();
19472     },
19473     
19474     onTouchViewBeforeLoad : function(combo,opts)
19475     {
19476         return;
19477     },
19478
19479     // private
19480     onTouchViewLoad : function()
19481     {
19482         if(this.store.getCount() < 1){
19483             this.onTouchViewEmptyResults();
19484             return;
19485         }
19486         
19487         this.clearTouchView();
19488         
19489         var rawValue = this.getRawValue();
19490         
19491         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19492         
19493         this.tickItems = [];
19494         
19495         this.store.data.each(function(d, rowIndex){
19496             var row = this.touchViewListGroup.createChild(template);
19497             
19498             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19499                 row.addClass(d.data.cls);
19500             }
19501             
19502             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19503                 var cfg = {
19504                     data : d.data,
19505                     html : d.data[this.displayField]
19506                 };
19507                 
19508                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19509                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19510                 }
19511             }
19512             row.removeClass('selected');
19513             if(!this.multiple && this.valueField &&
19514                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19515             {
19516                 // radio buttons..
19517                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19518                 row.addClass('selected');
19519             }
19520             
19521             if(this.multiple && this.valueField &&
19522                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19523             {
19524                 
19525                 // checkboxes...
19526                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19527                 this.tickItems.push(d.data);
19528             }
19529             
19530             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19531             
19532         }, this);
19533         
19534         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19535         
19536         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19537
19538         if(this.modalTitle.length){
19539             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19540         }
19541
19542         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19543         
19544         if(this.mobile_restrict_height && listHeight < bodyHeight){
19545             this.touchViewBodyEl.setHeight(listHeight);
19546         }
19547         
19548         var _this = this;
19549         
19550         if(firstChecked && listHeight > bodyHeight){
19551             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19552         }
19553         
19554     },
19555     
19556     onTouchViewLoadException : function()
19557     {
19558         this.hideTouchView();
19559     },
19560     
19561     onTouchViewEmptyResults : function()
19562     {
19563         this.clearTouchView();
19564         
19565         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19566         
19567         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19568         
19569     },
19570     
19571     clearTouchView : function()
19572     {
19573         this.touchViewListGroup.dom.innerHTML = '';
19574     },
19575     
19576     onTouchViewClick : function(e, el, o)
19577     {
19578         e.preventDefault();
19579         
19580         var row = o.row;
19581         var rowIndex = o.rowIndex;
19582         
19583         var r = this.store.getAt(rowIndex);
19584         
19585         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19586             
19587             if(!this.multiple){
19588                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19589                     c.dom.removeAttribute('checked');
19590                 }, this);
19591
19592                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19593
19594                 this.setFromData(r.data);
19595
19596                 var close = this.closeTriggerEl();
19597
19598                 if(close){
19599                     close.show();
19600                 }
19601
19602                 this.hideTouchView();
19603
19604                 this.fireEvent('select', this, r, rowIndex);
19605
19606                 return;
19607             }
19608
19609             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19610                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19611                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19612                 return;
19613             }
19614
19615             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19616             this.addItem(r.data);
19617             this.tickItems.push(r.data);
19618         }
19619     },
19620     
19621     getAutoCreateNativeIOS : function()
19622     {
19623         var cfg = {
19624             cls: 'form-group' //input-group,
19625         };
19626         
19627         var combobox =  {
19628             tag: 'select',
19629             cls : 'roo-ios-select'
19630         };
19631         
19632         if (this.name) {
19633             combobox.name = this.name;
19634         }
19635         
19636         if (this.disabled) {
19637             combobox.disabled = true;
19638         }
19639         
19640         var settings = this;
19641         
19642         ['xs','sm','md','lg'].map(function(size){
19643             if (settings[size]) {
19644                 cfg.cls += ' col-' + size + '-' + settings[size];
19645             }
19646         });
19647         
19648         cfg.cn = combobox;
19649         
19650         return cfg;
19651         
19652     },
19653     
19654     initIOSView : function()
19655     {
19656         this.store.on('load', this.onIOSViewLoad, this);
19657         
19658         return;
19659     },
19660     
19661     onIOSViewLoad : function()
19662     {
19663         if(this.store.getCount() < 1){
19664             return;
19665         }
19666         
19667         this.clearIOSView();
19668         
19669         if(this.allowBlank) {
19670             
19671             var default_text = '-- SELECT --';
19672             
19673             if(this.placeholder.length){
19674                 default_text = this.placeholder;
19675             }
19676             
19677             if(this.emptyTitle.length){
19678                 default_text += ' - ' + this.emptyTitle + ' -';
19679             }
19680             
19681             var opt = this.inputEl().createChild({
19682                 tag: 'option',
19683                 value : 0,
19684                 html : default_text
19685             });
19686             
19687             var o = {};
19688             o[this.valueField] = 0;
19689             o[this.displayField] = default_text;
19690             
19691             this.ios_options.push({
19692                 data : o,
19693                 el : opt
19694             });
19695             
19696         }
19697         
19698         this.store.data.each(function(d, rowIndex){
19699             
19700             var html = '';
19701             
19702             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19703                 html = d.data[this.displayField];
19704             }
19705             
19706             var value = '';
19707             
19708             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19709                 value = d.data[this.valueField];
19710             }
19711             
19712             var option = {
19713                 tag: 'option',
19714                 value : value,
19715                 html : html
19716             };
19717             
19718             if(this.value == d.data[this.valueField]){
19719                 option['selected'] = true;
19720             }
19721             
19722             var opt = this.inputEl().createChild(option);
19723             
19724             this.ios_options.push({
19725                 data : d.data,
19726                 el : opt
19727             });
19728             
19729         }, this);
19730         
19731         this.inputEl().on('change', function(){
19732            this.fireEvent('select', this);
19733         }, this);
19734         
19735     },
19736     
19737     clearIOSView: function()
19738     {
19739         this.inputEl().dom.innerHTML = '';
19740         
19741         this.ios_options = [];
19742     },
19743     
19744     setIOSValue: function(v)
19745     {
19746         this.value = v;
19747         
19748         if(!this.ios_options){
19749             return;
19750         }
19751         
19752         Roo.each(this.ios_options, function(opts){
19753            
19754            opts.el.dom.removeAttribute('selected');
19755            
19756            if(opts.data[this.valueField] != v){
19757                return;
19758            }
19759            
19760            opts.el.dom.setAttribute('selected', true);
19761            
19762         }, this);
19763     }
19764
19765     /** 
19766     * @cfg {Boolean} grow 
19767     * @hide 
19768     */
19769     /** 
19770     * @cfg {Number} growMin 
19771     * @hide 
19772     */
19773     /** 
19774     * @cfg {Number} growMax 
19775     * @hide 
19776     */
19777     /**
19778      * @hide
19779      * @method autoSize
19780      */
19781 });
19782
19783 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19784     
19785     header : {
19786         tag: 'div',
19787         cls: 'modal-header',
19788         cn: [
19789             {
19790                 tag: 'h4',
19791                 cls: 'modal-title'
19792             }
19793         ]
19794     },
19795     
19796     body : {
19797         tag: 'div',
19798         cls: 'modal-body',
19799         cn: [
19800             {
19801                 tag: 'ul',
19802                 cls: 'list-group'
19803             }
19804         ]
19805     },
19806     
19807     listItemRadio : {
19808         tag: 'li',
19809         cls: 'list-group-item',
19810         cn: [
19811             {
19812                 tag: 'span',
19813                 cls: 'roo-combobox-list-group-item-value'
19814             },
19815             {
19816                 tag: 'div',
19817                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19818                 cn: [
19819                     {
19820                         tag: 'input',
19821                         type: 'radio'
19822                     },
19823                     {
19824                         tag: 'label'
19825                     }
19826                 ]
19827             }
19828         ]
19829     },
19830     
19831     listItemCheckbox : {
19832         tag: 'li',
19833         cls: 'list-group-item',
19834         cn: [
19835             {
19836                 tag: 'span',
19837                 cls: 'roo-combobox-list-group-item-value'
19838             },
19839             {
19840                 tag: 'div',
19841                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19842                 cn: [
19843                     {
19844                         tag: 'input',
19845                         type: 'checkbox'
19846                     },
19847                     {
19848                         tag: 'label'
19849                     }
19850                 ]
19851             }
19852         ]
19853     },
19854     
19855     emptyResult : {
19856         tag: 'div',
19857         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19858     },
19859     
19860     footer : {
19861         tag: 'div',
19862         cls: 'modal-footer',
19863         cn: [
19864             {
19865                 tag: 'div',
19866                 cls: 'row',
19867                 cn: [
19868                     {
19869                         tag: 'div',
19870                         cls: 'col-xs-6 text-left',
19871                         cn: {
19872                             tag: 'button',
19873                             cls: 'btn btn-danger roo-touch-view-cancel',
19874                             html: 'Cancel'
19875                         }
19876                     },
19877                     {
19878                         tag: 'div',
19879                         cls: 'col-xs-6 text-right',
19880                         cn: {
19881                             tag: 'button',
19882                             cls: 'btn btn-success roo-touch-view-ok',
19883                             html: 'OK'
19884                         }
19885                     }
19886                 ]
19887             }
19888         ]
19889         
19890     }
19891 });
19892
19893 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19894     
19895     touchViewTemplate : {
19896         tag: 'div',
19897         cls: 'modal fade roo-combobox-touch-view',
19898         cn: [
19899             {
19900                 tag: 'div',
19901                 cls: 'modal-dialog',
19902                 style : 'position:fixed', // we have to fix position....
19903                 cn: [
19904                     {
19905                         tag: 'div',
19906                         cls: 'modal-content',
19907                         cn: [
19908                             Roo.bootstrap.form.ComboBox.header,
19909                             Roo.bootstrap.form.ComboBox.body,
19910                             Roo.bootstrap.form.ComboBox.footer
19911                         ]
19912                     }
19913                 ]
19914             }
19915         ]
19916     }
19917 });/*
19918  * Based on:
19919  * Ext JS Library 1.1.1
19920  * Copyright(c) 2006-2007, Ext JS, LLC.
19921  *
19922  * Originally Released Under LGPL - original licence link has changed is not relivant.
19923  *
19924  * Fork - LGPL
19925  * <script type="text/javascript">
19926  */
19927
19928 /**
19929  * @class Roo.View
19930  * @extends Roo.util.Observable
19931  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19932  * This class also supports single and multi selection modes. <br>
19933  * Create a data model bound view:
19934  <pre><code>
19935  var store = new Roo.data.Store(...);
19936
19937  var view = new Roo.View({
19938     el : "my-element",
19939     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19940  
19941     singleSelect: true,
19942     selectedClass: "ydataview-selected",
19943     store: store
19944  });
19945
19946  // listen for node click?
19947  view.on("click", function(vw, index, node, e){
19948  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19949  });
19950
19951  // load XML data
19952  dataModel.load("foobar.xml");
19953  </code></pre>
19954  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19955  * <br><br>
19956  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19957  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19958  * 
19959  * Note: old style constructor is still suported (container, template, config)
19960  * 
19961  * @constructor
19962  * Create a new View
19963  * @param {Object} config The config object
19964  * 
19965  */
19966 Roo.View = function(config, depreciated_tpl, depreciated_config){
19967     
19968     this.parent = false;
19969     
19970     if (typeof(depreciated_tpl) == 'undefined') {
19971         // new way.. - universal constructor.
19972         Roo.apply(this, config);
19973         this.el  = Roo.get(this.el);
19974     } else {
19975         // old format..
19976         this.el  = Roo.get(config);
19977         this.tpl = depreciated_tpl;
19978         Roo.apply(this, depreciated_config);
19979     }
19980     this.wrapEl  = this.el.wrap().wrap();
19981     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19982     
19983     
19984     if(typeof(this.tpl) == "string"){
19985         this.tpl = new Roo.Template(this.tpl);
19986     } else {
19987         // support xtype ctors..
19988         this.tpl = new Roo.factory(this.tpl, Roo);
19989     }
19990     
19991     
19992     this.tpl.compile();
19993     
19994     /** @private */
19995     this.addEvents({
19996         /**
19997          * @event beforeclick
19998          * Fires before a click is processed. Returns false to cancel the default action.
19999          * @param {Roo.View} this
20000          * @param {Number} index The index of the target node
20001          * @param {HTMLElement} node The target node
20002          * @param {Roo.EventObject} e The raw event object
20003          */
20004             "beforeclick" : true,
20005         /**
20006          * @event click
20007          * Fires when a template node is clicked.
20008          * @param {Roo.View} this
20009          * @param {Number} index The index of the target node
20010          * @param {HTMLElement} node The target node
20011          * @param {Roo.EventObject} e The raw event object
20012          */
20013             "click" : true,
20014         /**
20015          * @event dblclick
20016          * Fires when a template node is double clicked.
20017          * @param {Roo.View} this
20018          * @param {Number} index The index of the target node
20019          * @param {HTMLElement} node The target node
20020          * @param {Roo.EventObject} e The raw event object
20021          */
20022             "dblclick" : true,
20023         /**
20024          * @event contextmenu
20025          * Fires when a template node is right clicked.
20026          * @param {Roo.View} this
20027          * @param {Number} index The index of the target node
20028          * @param {HTMLElement} node The target node
20029          * @param {Roo.EventObject} e The raw event object
20030          */
20031             "contextmenu" : true,
20032         /**
20033          * @event selectionchange
20034          * Fires when the selected nodes change.
20035          * @param {Roo.View} this
20036          * @param {Array} selections Array of the selected nodes
20037          */
20038             "selectionchange" : true,
20039     
20040         /**
20041          * @event beforeselect
20042          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20043          * @param {Roo.View} this
20044          * @param {HTMLElement} node The node to be selected
20045          * @param {Array} selections Array of currently selected nodes
20046          */
20047             "beforeselect" : true,
20048         /**
20049          * @event preparedata
20050          * Fires on every row to render, to allow you to change the data.
20051          * @param {Roo.View} this
20052          * @param {Object} data to be rendered (change this)
20053          */
20054           "preparedata" : true
20055           
20056           
20057         });
20058
20059
20060
20061     this.el.on({
20062         "click": this.onClick,
20063         "dblclick": this.onDblClick,
20064         "contextmenu": this.onContextMenu,
20065         scope:this
20066     });
20067
20068     this.selections = [];
20069     this.nodes = [];
20070     this.cmp = new Roo.CompositeElementLite([]);
20071     if(this.store){
20072         this.store = Roo.factory(this.store, Roo.data);
20073         this.setStore(this.store, true);
20074     }
20075     
20076     if ( this.footer && this.footer.xtype) {
20077            
20078          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20079         
20080         this.footer.dataSource = this.store;
20081         this.footer.container = fctr;
20082         this.footer = Roo.factory(this.footer, Roo);
20083         fctr.insertFirst(this.el);
20084         
20085         // this is a bit insane - as the paging toolbar seems to detach the el..
20086 //        dom.parentNode.parentNode.parentNode
20087          // they get detached?
20088     }
20089     
20090     
20091     Roo.View.superclass.constructor.call(this);
20092     
20093     
20094 };
20095
20096 Roo.extend(Roo.View, Roo.util.Observable, {
20097     
20098      /**
20099      * @cfg {Roo.data.Store} store Data store to load data from.
20100      */
20101     store : false,
20102     
20103     /**
20104      * @cfg {String|Roo.Element} el The container element.
20105      */
20106     el : '',
20107     
20108     /**
20109      * @cfg {String|Roo.Template} tpl The template used by this View 
20110      */
20111     tpl : false,
20112     /**
20113      * @cfg {String} dataName the named area of the template to use as the data area
20114      *                          Works with domtemplates roo-name="name"
20115      */
20116     dataName: false,
20117     /**
20118      * @cfg {String} selectedClass The css class to add to selected nodes
20119      */
20120     selectedClass : "x-view-selected",
20121      /**
20122      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20123      */
20124     emptyText : "",
20125     
20126     /**
20127      * @cfg {String} text to display on mask (default Loading)
20128      */
20129     mask : false,
20130     /**
20131      * @cfg {Boolean} multiSelect Allow multiple selection
20132      */
20133     multiSelect : false,
20134     /**
20135      * @cfg {Boolean} singleSelect Allow single selection
20136      */
20137     singleSelect:  false,
20138     
20139     /**
20140      * @cfg {Boolean} toggleSelect - selecting 
20141      */
20142     toggleSelect : false,
20143     
20144     /**
20145      * @cfg {Boolean} tickable - selecting 
20146      */
20147     tickable : false,
20148     
20149     /**
20150      * Returns the element this view is bound to.
20151      * @return {Roo.Element}
20152      */
20153     getEl : function(){
20154         return this.wrapEl;
20155     },
20156     
20157     
20158
20159     /**
20160      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20161      */
20162     refresh : function(){
20163         //Roo.log('refresh');
20164         var t = this.tpl;
20165         
20166         // if we are using something like 'domtemplate', then
20167         // the what gets used is:
20168         // t.applySubtemplate(NAME, data, wrapping data..)
20169         // the outer template then get' applied with
20170         //     the store 'extra data'
20171         // and the body get's added to the
20172         //      roo-name="data" node?
20173         //      <span class='roo-tpl-{name}'></span> ?????
20174         
20175         
20176         
20177         this.clearSelections();
20178         this.el.update("");
20179         var html = [];
20180         var records = this.store.getRange();
20181         if(records.length < 1) {
20182             
20183             // is this valid??  = should it render a template??
20184             
20185             this.el.update(this.emptyText);
20186             return;
20187         }
20188         var el = this.el;
20189         if (this.dataName) {
20190             this.el.update(t.apply(this.store.meta)); //????
20191             el = this.el.child('.roo-tpl-' + this.dataName);
20192         }
20193         
20194         for(var i = 0, len = records.length; i < len; i++){
20195             var data = this.prepareData(records[i].data, i, records[i]);
20196             this.fireEvent("preparedata", this, data, i, records[i]);
20197             
20198             var d = Roo.apply({}, data);
20199             
20200             if(this.tickable){
20201                 Roo.apply(d, {'roo-id' : Roo.id()});
20202                 
20203                 var _this = this;
20204             
20205                 Roo.each(this.parent.item, function(item){
20206                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20207                         return;
20208                     }
20209                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20210                 });
20211             }
20212             
20213             html[html.length] = Roo.util.Format.trim(
20214                 this.dataName ?
20215                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20216                     t.apply(d)
20217             );
20218         }
20219         
20220         
20221         
20222         el.update(html.join(""));
20223         this.nodes = el.dom.childNodes;
20224         this.updateIndexes(0);
20225     },
20226     
20227
20228     /**
20229      * Function to override to reformat the data that is sent to
20230      * the template for each node.
20231      * DEPRICATED - use the preparedata event handler.
20232      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20233      * a JSON object for an UpdateManager bound view).
20234      */
20235     prepareData : function(data, index, record)
20236     {
20237         this.fireEvent("preparedata", this, data, index, record);
20238         return data;
20239     },
20240
20241     onUpdate : function(ds, record){
20242         // Roo.log('on update');   
20243         this.clearSelections();
20244         var index = this.store.indexOf(record);
20245         var n = this.nodes[index];
20246         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20247         n.parentNode.removeChild(n);
20248         this.updateIndexes(index, index);
20249     },
20250
20251     
20252     
20253 // --------- FIXME     
20254     onAdd : function(ds, records, index)
20255     {
20256         //Roo.log(['on Add', ds, records, index] );        
20257         this.clearSelections();
20258         if(this.nodes.length == 0){
20259             this.refresh();
20260             return;
20261         }
20262         var n = this.nodes[index];
20263         for(var i = 0, len = records.length; i < len; i++){
20264             var d = this.prepareData(records[i].data, i, records[i]);
20265             if(n){
20266                 this.tpl.insertBefore(n, d);
20267             }else{
20268                 
20269                 this.tpl.append(this.el, d);
20270             }
20271         }
20272         this.updateIndexes(index);
20273     },
20274
20275     onRemove : function(ds, record, index){
20276        // Roo.log('onRemove');
20277         this.clearSelections();
20278         var el = this.dataName  ?
20279             this.el.child('.roo-tpl-' + this.dataName) :
20280             this.el; 
20281         
20282         el.dom.removeChild(this.nodes[index]);
20283         this.updateIndexes(index);
20284     },
20285
20286     /**
20287      * Refresh an individual node.
20288      * @param {Number} index
20289      */
20290     refreshNode : function(index){
20291         this.onUpdate(this.store, this.store.getAt(index));
20292     },
20293
20294     updateIndexes : function(startIndex, endIndex){
20295         var ns = this.nodes;
20296         startIndex = startIndex || 0;
20297         endIndex = endIndex || ns.length - 1;
20298         for(var i = startIndex; i <= endIndex; i++){
20299             ns[i].nodeIndex = i;
20300         }
20301     },
20302
20303     /**
20304      * Changes the data store this view uses and refresh the view.
20305      * @param {Store} store
20306      */
20307     setStore : function(store, initial){
20308         if(!initial && this.store){
20309             this.store.un("datachanged", this.refresh);
20310             this.store.un("add", this.onAdd);
20311             this.store.un("remove", this.onRemove);
20312             this.store.un("update", this.onUpdate);
20313             this.store.un("clear", this.refresh);
20314             this.store.un("beforeload", this.onBeforeLoad);
20315             this.store.un("load", this.onLoad);
20316             this.store.un("loadexception", this.onLoad);
20317         }
20318         if(store){
20319           
20320             store.on("datachanged", this.refresh, this);
20321             store.on("add", this.onAdd, this);
20322             store.on("remove", this.onRemove, this);
20323             store.on("update", this.onUpdate, this);
20324             store.on("clear", this.refresh, this);
20325             store.on("beforeload", this.onBeforeLoad, this);
20326             store.on("load", this.onLoad, this);
20327             store.on("loadexception", this.onLoad, this);
20328         }
20329         
20330         if(store){
20331             this.refresh();
20332         }
20333     },
20334     /**
20335      * onbeforeLoad - masks the loading area.
20336      *
20337      */
20338     onBeforeLoad : function(store,opts)
20339     {
20340          //Roo.log('onBeforeLoad');   
20341         if (!opts.add) {
20342             this.el.update("");
20343         }
20344         this.el.mask(this.mask ? this.mask : "Loading" ); 
20345     },
20346     onLoad : function ()
20347     {
20348         this.el.unmask();
20349     },
20350     
20351
20352     /**
20353      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20354      * @param {HTMLElement} node
20355      * @return {HTMLElement} The template node
20356      */
20357     findItemFromChild : function(node){
20358         var el = this.dataName  ?
20359             this.el.child('.roo-tpl-' + this.dataName,true) :
20360             this.el.dom; 
20361         
20362         if(!node || node.parentNode == el){
20363                     return node;
20364             }
20365             var p = node.parentNode;
20366             while(p && p != el){
20367             if(p.parentNode == el){
20368                 return p;
20369             }
20370             p = p.parentNode;
20371         }
20372             return null;
20373     },
20374
20375     /** @ignore */
20376     onClick : function(e){
20377         var item = this.findItemFromChild(e.getTarget());
20378         if(item){
20379             var index = this.indexOf(item);
20380             if(this.onItemClick(item, index, e) !== false){
20381                 this.fireEvent("click", this, index, item, e);
20382             }
20383         }else{
20384             this.clearSelections();
20385         }
20386     },
20387
20388     /** @ignore */
20389     onContextMenu : function(e){
20390         var item = this.findItemFromChild(e.getTarget());
20391         if(item){
20392             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20393         }
20394     },
20395
20396     /** @ignore */
20397     onDblClick : function(e){
20398         var item = this.findItemFromChild(e.getTarget());
20399         if(item){
20400             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20401         }
20402     },
20403
20404     onItemClick : function(item, index, e)
20405     {
20406         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20407             return false;
20408         }
20409         if (this.toggleSelect) {
20410             var m = this.isSelected(item) ? 'unselect' : 'select';
20411             //Roo.log(m);
20412             var _t = this;
20413             _t[m](item, true, false);
20414             return true;
20415         }
20416         if(this.multiSelect || this.singleSelect){
20417             if(this.multiSelect && e.shiftKey && this.lastSelection){
20418                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20419             }else{
20420                 this.select(item, this.multiSelect && e.ctrlKey);
20421                 this.lastSelection = item;
20422             }
20423             
20424             if(!this.tickable){
20425                 e.preventDefault();
20426             }
20427             
20428         }
20429         return true;
20430     },
20431
20432     /**
20433      * Get the number of selected nodes.
20434      * @return {Number}
20435      */
20436     getSelectionCount : function(){
20437         return this.selections.length;
20438     },
20439
20440     /**
20441      * Get the currently selected nodes.
20442      * @return {Array} An array of HTMLElements
20443      */
20444     getSelectedNodes : function(){
20445         return this.selections;
20446     },
20447
20448     /**
20449      * Get the indexes of the selected nodes.
20450      * @return {Array}
20451      */
20452     getSelectedIndexes : function(){
20453         var indexes = [], s = this.selections;
20454         for(var i = 0, len = s.length; i < len; i++){
20455             indexes.push(s[i].nodeIndex);
20456         }
20457         return indexes;
20458     },
20459
20460     /**
20461      * Clear all selections
20462      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20463      */
20464     clearSelections : function(suppressEvent){
20465         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20466             this.cmp.elements = this.selections;
20467             this.cmp.removeClass(this.selectedClass);
20468             this.selections = [];
20469             if(!suppressEvent){
20470                 this.fireEvent("selectionchange", this, this.selections);
20471             }
20472         }
20473     },
20474
20475     /**
20476      * Returns true if the passed node is selected
20477      * @param {HTMLElement/Number} node The node or node index
20478      * @return {Boolean}
20479      */
20480     isSelected : function(node){
20481         var s = this.selections;
20482         if(s.length < 1){
20483             return false;
20484         }
20485         node = this.getNode(node);
20486         return s.indexOf(node) !== -1;
20487     },
20488
20489     /**
20490      * Selects nodes.
20491      * @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
20492      * @param {Boolean} keepExisting (optional) true to keep existing selections
20493      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20494      */
20495     select : function(nodeInfo, keepExisting, suppressEvent){
20496         if(nodeInfo instanceof Array){
20497             if(!keepExisting){
20498                 this.clearSelections(true);
20499             }
20500             for(var i = 0, len = nodeInfo.length; i < len; i++){
20501                 this.select(nodeInfo[i], true, true);
20502             }
20503             return;
20504         } 
20505         var node = this.getNode(nodeInfo);
20506         if(!node || this.isSelected(node)){
20507             return; // already selected.
20508         }
20509         if(!keepExisting){
20510             this.clearSelections(true);
20511         }
20512         
20513         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20514             Roo.fly(node).addClass(this.selectedClass);
20515             this.selections.push(node);
20516             if(!suppressEvent){
20517                 this.fireEvent("selectionchange", this, this.selections);
20518             }
20519         }
20520         
20521         
20522     },
20523       /**
20524      * Unselects nodes.
20525      * @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
20526      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20527      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20528      */
20529     unselect : function(nodeInfo, keepExisting, suppressEvent)
20530     {
20531         if(nodeInfo instanceof Array){
20532             Roo.each(this.selections, function(s) {
20533                 this.unselect(s, nodeInfo);
20534             }, this);
20535             return;
20536         }
20537         var node = this.getNode(nodeInfo);
20538         if(!node || !this.isSelected(node)){
20539             //Roo.log("not selected");
20540             return; // not selected.
20541         }
20542         // fireevent???
20543         var ns = [];
20544         Roo.each(this.selections, function(s) {
20545             if (s == node ) {
20546                 Roo.fly(node).removeClass(this.selectedClass);
20547
20548                 return;
20549             }
20550             ns.push(s);
20551         },this);
20552         
20553         this.selections= ns;
20554         this.fireEvent("selectionchange", this, this.selections);
20555     },
20556
20557     /**
20558      * Gets a template node.
20559      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20560      * @return {HTMLElement} The node or null if it wasn't found
20561      */
20562     getNode : function(nodeInfo){
20563         if(typeof nodeInfo == "string"){
20564             return document.getElementById(nodeInfo);
20565         }else if(typeof nodeInfo == "number"){
20566             return this.nodes[nodeInfo];
20567         }
20568         return nodeInfo;
20569     },
20570
20571     /**
20572      * Gets a range template nodes.
20573      * @param {Number} startIndex
20574      * @param {Number} endIndex
20575      * @return {Array} An array of nodes
20576      */
20577     getNodes : function(start, end){
20578         var ns = this.nodes;
20579         start = start || 0;
20580         end = typeof end == "undefined" ? ns.length - 1 : end;
20581         var nodes = [];
20582         if(start <= end){
20583             for(var i = start; i <= end; i++){
20584                 nodes.push(ns[i]);
20585             }
20586         } else{
20587             for(var i = start; i >= end; i--){
20588                 nodes.push(ns[i]);
20589             }
20590         }
20591         return nodes;
20592     },
20593
20594     /**
20595      * Finds the index of the passed node
20596      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20597      * @return {Number} The index of the node or -1
20598      */
20599     indexOf : function(node){
20600         node = this.getNode(node);
20601         if(typeof node.nodeIndex == "number"){
20602             return node.nodeIndex;
20603         }
20604         var ns = this.nodes;
20605         for(var i = 0, len = ns.length; i < len; i++){
20606             if(ns[i] == node){
20607                 return i;
20608             }
20609         }
20610         return -1;
20611     }
20612 });
20613 /*
20614  * - LGPL
20615  *
20616  * based on jquery fullcalendar
20617  * 
20618  */
20619
20620 Roo.bootstrap = Roo.bootstrap || {};
20621 /**
20622  * @class Roo.bootstrap.Calendar
20623  * @extends Roo.bootstrap.Component
20624  * Bootstrap Calendar class
20625  * @cfg {Boolean} loadMask (true|false) default false
20626  * @cfg {Object} header generate the user specific header of the calendar, default false
20627
20628  * @constructor
20629  * Create a new Container
20630  * @param {Object} config The config object
20631  */
20632
20633
20634
20635 Roo.bootstrap.Calendar = function(config){
20636     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20637      this.addEvents({
20638         /**
20639              * @event select
20640              * Fires when a date is selected
20641              * @param {DatePicker} this
20642              * @param {Date} date The selected date
20643              */
20644         'select': true,
20645         /**
20646              * @event monthchange
20647              * Fires when the displayed month changes 
20648              * @param {DatePicker} this
20649              * @param {Date} date The selected month
20650              */
20651         'monthchange': true,
20652         /**
20653              * @event evententer
20654              * Fires when mouse over an event
20655              * @param {Calendar} this
20656              * @param {event} Event
20657              */
20658         'evententer': true,
20659         /**
20660              * @event eventleave
20661              * Fires when the mouse leaves an
20662              * @param {Calendar} this
20663              * @param {event}
20664              */
20665         'eventleave': true,
20666         /**
20667              * @event eventclick
20668              * Fires when the mouse click an
20669              * @param {Calendar} this
20670              * @param {event}
20671              */
20672         'eventclick': true
20673         
20674     });
20675
20676 };
20677
20678 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20679     
20680           /**
20681      * @cfg {Roo.data.Store} store
20682      * The data source for the calendar
20683      */
20684         store : false,
20685      /**
20686      * @cfg {Number} startDay
20687      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20688      */
20689     startDay : 0,
20690     
20691     loadMask : false,
20692     
20693     header : false,
20694       
20695     getAutoCreate : function(){
20696         
20697         
20698         var fc_button = function(name, corner, style, content ) {
20699             return Roo.apply({},{
20700                 tag : 'span',
20701                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20702                          (corner.length ?
20703                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20704                             ''
20705                         ),
20706                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20707                 unselectable: 'on'
20708             });
20709         };
20710         
20711         var header = {};
20712         
20713         if(!this.header){
20714             header = {
20715                 tag : 'table',
20716                 cls : 'fc-header',
20717                 style : 'width:100%',
20718                 cn : [
20719                     {
20720                         tag: 'tr',
20721                         cn : [
20722                             {
20723                                 tag : 'td',
20724                                 cls : 'fc-header-left',
20725                                 cn : [
20726                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20727                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20728                                     { tag: 'span', cls: 'fc-header-space' },
20729                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20730
20731
20732                                 ]
20733                             },
20734
20735                             {
20736                                 tag : 'td',
20737                                 cls : 'fc-header-center',
20738                                 cn : [
20739                                     {
20740                                         tag: 'span',
20741                                         cls: 'fc-header-title',
20742                                         cn : {
20743                                             tag: 'H2',
20744                                             html : 'month / year'
20745                                         }
20746                                     }
20747
20748                                 ]
20749                             },
20750                             {
20751                                 tag : 'td',
20752                                 cls : 'fc-header-right',
20753                                 cn : [
20754                               /*      fc_button('month', 'left', '', 'month' ),
20755                                     fc_button('week', '', '', 'week' ),
20756                                     fc_button('day', 'right', '', 'day' )
20757                                 */    
20758
20759                                 ]
20760                             }
20761
20762                         ]
20763                     }
20764                 ]
20765             };
20766         }
20767         
20768         header = this.header;
20769         
20770        
20771         var cal_heads = function() {
20772             var ret = [];
20773             // fixme - handle this.
20774             
20775             for (var i =0; i < Date.dayNames.length; i++) {
20776                 var d = Date.dayNames[i];
20777                 ret.push({
20778                     tag: 'th',
20779                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20780                     html : d.substring(0,3)
20781                 });
20782                 
20783             }
20784             ret[0].cls += ' fc-first';
20785             ret[6].cls += ' fc-last';
20786             return ret;
20787         };
20788         var cal_cell = function(n) {
20789             return  {
20790                 tag: 'td',
20791                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20792                 cn : [
20793                     {
20794                         cn : [
20795                             {
20796                                 cls: 'fc-day-number',
20797                                 html: 'D'
20798                             },
20799                             {
20800                                 cls: 'fc-day-content',
20801                              
20802                                 cn : [
20803                                      {
20804                                         style: 'position: relative;' // height: 17px;
20805                                     }
20806                                 ]
20807                             }
20808                             
20809                             
20810                         ]
20811                     }
20812                 ]
20813                 
20814             }
20815         };
20816         var cal_rows = function() {
20817             
20818             var ret = [];
20819             for (var r = 0; r < 6; r++) {
20820                 var row= {
20821                     tag : 'tr',
20822                     cls : 'fc-week',
20823                     cn : []
20824                 };
20825                 
20826                 for (var i =0; i < Date.dayNames.length; i++) {
20827                     var d = Date.dayNames[i];
20828                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20829
20830                 }
20831                 row.cn[0].cls+=' fc-first';
20832                 row.cn[0].cn[0].style = 'min-height:90px';
20833                 row.cn[6].cls+=' fc-last';
20834                 ret.push(row);
20835                 
20836             }
20837             ret[0].cls += ' fc-first';
20838             ret[4].cls += ' fc-prev-last';
20839             ret[5].cls += ' fc-last';
20840             return ret;
20841             
20842         };
20843         
20844         var cal_table = {
20845             tag: 'table',
20846             cls: 'fc-border-separate',
20847             style : 'width:100%',
20848             cellspacing  : 0,
20849             cn : [
20850                 { 
20851                     tag: 'thead',
20852                     cn : [
20853                         { 
20854                             tag: 'tr',
20855                             cls : 'fc-first fc-last',
20856                             cn : cal_heads()
20857                         }
20858                     ]
20859                 },
20860                 { 
20861                     tag: 'tbody',
20862                     cn : cal_rows()
20863                 }
20864                   
20865             ]
20866         };
20867          
20868          var cfg = {
20869             cls : 'fc fc-ltr',
20870             cn : [
20871                 header,
20872                 {
20873                     cls : 'fc-content',
20874                     style : "position: relative;",
20875                     cn : [
20876                         {
20877                             cls : 'fc-view fc-view-month fc-grid',
20878                             style : 'position: relative',
20879                             unselectable : 'on',
20880                             cn : [
20881                                 {
20882                                     cls : 'fc-event-container',
20883                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20884                                 },
20885                                 cal_table
20886                             ]
20887                         }
20888                     ]
20889     
20890                 }
20891            ] 
20892             
20893         };
20894         
20895          
20896         
20897         return cfg;
20898     },
20899     
20900     
20901     initEvents : function()
20902     {
20903         if(!this.store){
20904             throw "can not find store for calendar";
20905         }
20906         
20907         var mark = {
20908             tag: "div",
20909             cls:"x-dlg-mask",
20910             style: "text-align:center",
20911             cn: [
20912                 {
20913                     tag: "div",
20914                     style: "background-color:white;width:50%;margin:250 auto",
20915                     cn: [
20916                         {
20917                             tag: "img",
20918                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20919                         },
20920                         {
20921                             tag: "span",
20922                             html: "Loading"
20923                         }
20924                         
20925                     ]
20926                 }
20927             ]
20928         };
20929         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20930         
20931         var size = this.el.select('.fc-content', true).first().getSize();
20932         this.maskEl.setSize(size.width, size.height);
20933         this.maskEl.enableDisplayMode("block");
20934         if(!this.loadMask){
20935             this.maskEl.hide();
20936         }
20937         
20938         this.store = Roo.factory(this.store, Roo.data);
20939         this.store.on('load', this.onLoad, this);
20940         this.store.on('beforeload', this.onBeforeLoad, this);
20941         
20942         this.resize();
20943         
20944         this.cells = this.el.select('.fc-day',true);
20945         //Roo.log(this.cells);
20946         this.textNodes = this.el.query('.fc-day-number');
20947         this.cells.addClassOnOver('fc-state-hover');
20948         
20949         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20950         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20951         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20952         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20953         
20954         this.on('monthchange', this.onMonthChange, this);
20955         
20956         this.update(new Date().clearTime());
20957     },
20958     
20959     resize : function() {
20960         var sz  = this.el.getSize();
20961         
20962         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20963         this.el.select('.fc-day-content div',true).setHeight(34);
20964     },
20965     
20966     
20967     // private
20968     showPrevMonth : function(e){
20969         this.update(this.activeDate.add("mo", -1));
20970     },
20971     showToday : function(e){
20972         this.update(new Date().clearTime());
20973     },
20974     // private
20975     showNextMonth : function(e){
20976         this.update(this.activeDate.add("mo", 1));
20977     },
20978
20979     // private
20980     showPrevYear : function(){
20981         this.update(this.activeDate.add("y", -1));
20982     },
20983
20984     // private
20985     showNextYear : function(){
20986         this.update(this.activeDate.add("y", 1));
20987     },
20988
20989     
20990    // private
20991     update : function(date)
20992     {
20993         var vd = this.activeDate;
20994         this.activeDate = date;
20995 //        if(vd && this.el){
20996 //            var t = date.getTime();
20997 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20998 //                Roo.log('using add remove');
20999 //                
21000 //                this.fireEvent('monthchange', this, date);
21001 //                
21002 //                this.cells.removeClass("fc-state-highlight");
21003 //                this.cells.each(function(c){
21004 //                   if(c.dateValue == t){
21005 //                       c.addClass("fc-state-highlight");
21006 //                       setTimeout(function(){
21007 //                            try{c.dom.firstChild.focus();}catch(e){}
21008 //                       }, 50);
21009 //                       return false;
21010 //                   }
21011 //                   return true;
21012 //                });
21013 //                return;
21014 //            }
21015 //        }
21016         
21017         var days = date.getDaysInMonth();
21018         
21019         var firstOfMonth = date.getFirstDateOfMonth();
21020         var startingPos = firstOfMonth.getDay()-this.startDay;
21021         
21022         if(startingPos < this.startDay){
21023             startingPos += 7;
21024         }
21025         
21026         var pm = date.add(Date.MONTH, -1);
21027         var prevStart = pm.getDaysInMonth()-startingPos;
21028 //        
21029         this.cells = this.el.select('.fc-day',true);
21030         this.textNodes = this.el.query('.fc-day-number');
21031         this.cells.addClassOnOver('fc-state-hover');
21032         
21033         var cells = this.cells.elements;
21034         var textEls = this.textNodes;
21035         
21036         Roo.each(cells, function(cell){
21037             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21038         });
21039         
21040         days += startingPos;
21041
21042         // convert everything to numbers so it's fast
21043         var day = 86400000;
21044         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21045         //Roo.log(d);
21046         //Roo.log(pm);
21047         //Roo.log(prevStart);
21048         
21049         var today = new Date().clearTime().getTime();
21050         var sel = date.clearTime().getTime();
21051         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21052         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21053         var ddMatch = this.disabledDatesRE;
21054         var ddText = this.disabledDatesText;
21055         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21056         var ddaysText = this.disabledDaysText;
21057         var format = this.format;
21058         
21059         var setCellClass = function(cal, cell){
21060             cell.row = 0;
21061             cell.events = [];
21062             cell.more = [];
21063             //Roo.log('set Cell Class');
21064             cell.title = "";
21065             var t = d.getTime();
21066             
21067             //Roo.log(d);
21068             
21069             cell.dateValue = t;
21070             if(t == today){
21071                 cell.className += " fc-today";
21072                 cell.className += " fc-state-highlight";
21073                 cell.title = cal.todayText;
21074             }
21075             if(t == sel){
21076                 // disable highlight in other month..
21077                 //cell.className += " fc-state-highlight";
21078                 
21079             }
21080             // disabling
21081             if(t < min) {
21082                 cell.className = " fc-state-disabled";
21083                 cell.title = cal.minText;
21084                 return;
21085             }
21086             if(t > max) {
21087                 cell.className = " fc-state-disabled";
21088                 cell.title = cal.maxText;
21089                 return;
21090             }
21091             if(ddays){
21092                 if(ddays.indexOf(d.getDay()) != -1){
21093                     cell.title = ddaysText;
21094                     cell.className = " fc-state-disabled";
21095                 }
21096             }
21097             if(ddMatch && format){
21098                 var fvalue = d.dateFormat(format);
21099                 if(ddMatch.test(fvalue)){
21100                     cell.title = ddText.replace("%0", fvalue);
21101                     cell.className = " fc-state-disabled";
21102                 }
21103             }
21104             
21105             if (!cell.initialClassName) {
21106                 cell.initialClassName = cell.dom.className;
21107             }
21108             
21109             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21110         };
21111
21112         var i = 0;
21113         
21114         for(; i < startingPos; i++) {
21115             textEls[i].innerHTML = (++prevStart);
21116             d.setDate(d.getDate()+1);
21117             
21118             cells[i].className = "fc-past fc-other-month";
21119             setCellClass(this, cells[i]);
21120         }
21121         
21122         var intDay = 0;
21123         
21124         for(; i < days; i++){
21125             intDay = i - startingPos + 1;
21126             textEls[i].innerHTML = (intDay);
21127             d.setDate(d.getDate()+1);
21128             
21129             cells[i].className = ''; // "x-date-active";
21130             setCellClass(this, cells[i]);
21131         }
21132         var extraDays = 0;
21133         
21134         for(; i < 42; i++) {
21135             textEls[i].innerHTML = (++extraDays);
21136             d.setDate(d.getDate()+1);
21137             
21138             cells[i].className = "fc-future fc-other-month";
21139             setCellClass(this, cells[i]);
21140         }
21141         
21142         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21143         
21144         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21145         
21146         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21147         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21148         
21149         if(totalRows != 6){
21150             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21151             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21152         }
21153         
21154         this.fireEvent('monthchange', this, date);
21155         
21156         
21157         /*
21158         if(!this.internalRender){
21159             var main = this.el.dom.firstChild;
21160             var w = main.offsetWidth;
21161             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21162             Roo.fly(main).setWidth(w);
21163             this.internalRender = true;
21164             // opera does not respect the auto grow header center column
21165             // then, after it gets a width opera refuses to recalculate
21166             // without a second pass
21167             if(Roo.isOpera && !this.secondPass){
21168                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21169                 this.secondPass = true;
21170                 this.update.defer(10, this, [date]);
21171             }
21172         }
21173         */
21174         
21175     },
21176     
21177     findCell : function(dt) {
21178         dt = dt.clearTime().getTime();
21179         var ret = false;
21180         this.cells.each(function(c){
21181             //Roo.log("check " +c.dateValue + '?=' + dt);
21182             if(c.dateValue == dt){
21183                 ret = c;
21184                 return false;
21185             }
21186             return true;
21187         });
21188         
21189         return ret;
21190     },
21191     
21192     findCells : function(ev) {
21193         var s = ev.start.clone().clearTime().getTime();
21194        // Roo.log(s);
21195         var e= ev.end.clone().clearTime().getTime();
21196        // Roo.log(e);
21197         var ret = [];
21198         this.cells.each(function(c){
21199              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21200             
21201             if(c.dateValue > e){
21202                 return ;
21203             }
21204             if(c.dateValue < s){
21205                 return ;
21206             }
21207             ret.push(c);
21208         });
21209         
21210         return ret;    
21211     },
21212     
21213 //    findBestRow: function(cells)
21214 //    {
21215 //        var ret = 0;
21216 //        
21217 //        for (var i =0 ; i < cells.length;i++) {
21218 //            ret  = Math.max(cells[i].rows || 0,ret);
21219 //        }
21220 //        return ret;
21221 //        
21222 //    },
21223     
21224     
21225     addItem : function(ev)
21226     {
21227         // look for vertical location slot in
21228         var cells = this.findCells(ev);
21229         
21230 //        ev.row = this.findBestRow(cells);
21231         
21232         // work out the location.
21233         
21234         var crow = false;
21235         var rows = [];
21236         for(var i =0; i < cells.length; i++) {
21237             
21238             cells[i].row = cells[0].row;
21239             
21240             if(i == 0){
21241                 cells[i].row = cells[i].row + 1;
21242             }
21243             
21244             if (!crow) {
21245                 crow = {
21246                     start : cells[i],
21247                     end :  cells[i]
21248                 };
21249                 continue;
21250             }
21251             if (crow.start.getY() == cells[i].getY()) {
21252                 // on same row.
21253                 crow.end = cells[i];
21254                 continue;
21255             }
21256             // different row.
21257             rows.push(crow);
21258             crow = {
21259                 start: cells[i],
21260                 end : cells[i]
21261             };
21262             
21263         }
21264         
21265         rows.push(crow);
21266         ev.els = [];
21267         ev.rows = rows;
21268         ev.cells = cells;
21269         
21270         cells[0].events.push(ev);
21271         
21272         this.calevents.push(ev);
21273     },
21274     
21275     clearEvents: function() {
21276         
21277         if(!this.calevents){
21278             return;
21279         }
21280         
21281         Roo.each(this.cells.elements, function(c){
21282             c.row = 0;
21283             c.events = [];
21284             c.more = [];
21285         });
21286         
21287         Roo.each(this.calevents, function(e) {
21288             Roo.each(e.els, function(el) {
21289                 el.un('mouseenter' ,this.onEventEnter, this);
21290                 el.un('mouseleave' ,this.onEventLeave, this);
21291                 el.remove();
21292             },this);
21293         },this);
21294         
21295         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21296             e.remove();
21297         });
21298         
21299     },
21300     
21301     renderEvents: function()
21302     {   
21303         var _this = this;
21304         
21305         this.cells.each(function(c) {
21306             
21307             if(c.row < 5){
21308                 return;
21309             }
21310             
21311             var ev = c.events;
21312             
21313             var r = 4;
21314             if(c.row != c.events.length){
21315                 r = 4 - (4 - (c.row - c.events.length));
21316             }
21317             
21318             c.events = ev.slice(0, r);
21319             c.more = ev.slice(r);
21320             
21321             if(c.more.length && c.more.length == 1){
21322                 c.events.push(c.more.pop());
21323             }
21324             
21325             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21326             
21327         });
21328             
21329         this.cells.each(function(c) {
21330             
21331             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21332             
21333             
21334             for (var e = 0; e < c.events.length; e++){
21335                 var ev = c.events[e];
21336                 var rows = ev.rows;
21337                 
21338                 for(var i = 0; i < rows.length; i++) {
21339                 
21340                     // how many rows should it span..
21341
21342                     var  cfg = {
21343                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21344                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21345
21346                         unselectable : "on",
21347                         cn : [
21348                             {
21349                                 cls: 'fc-event-inner',
21350                                 cn : [
21351     //                                {
21352     //                                  tag:'span',
21353     //                                  cls: 'fc-event-time',
21354     //                                  html : cells.length > 1 ? '' : ev.time
21355     //                                },
21356                                     {
21357                                       tag:'span',
21358                                       cls: 'fc-event-title',
21359                                       html : String.format('{0}', ev.title)
21360                                     }
21361
21362
21363                                 ]
21364                             },
21365                             {
21366                                 cls: 'ui-resizable-handle ui-resizable-e',
21367                                 html : '&nbsp;&nbsp;&nbsp'
21368                             }
21369
21370                         ]
21371                     };
21372
21373                     if (i == 0) {
21374                         cfg.cls += ' fc-event-start';
21375                     }
21376                     if ((i+1) == rows.length) {
21377                         cfg.cls += ' fc-event-end';
21378                     }
21379
21380                     var ctr = _this.el.select('.fc-event-container',true).first();
21381                     var cg = ctr.createChild(cfg);
21382
21383                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21384                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21385
21386                     var r = (c.more.length) ? 1 : 0;
21387                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21388                     cg.setWidth(ebox.right - sbox.x -2);
21389
21390                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21391                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21392                     cg.on('click', _this.onEventClick, _this, ev);
21393
21394                     ev.els.push(cg);
21395                     
21396                 }
21397                 
21398             }
21399             
21400             
21401             if(c.more.length){
21402                 var  cfg = {
21403                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21404                     style : 'position: absolute',
21405                     unselectable : "on",
21406                     cn : [
21407                         {
21408                             cls: 'fc-event-inner',
21409                             cn : [
21410                                 {
21411                                   tag:'span',
21412                                   cls: 'fc-event-title',
21413                                   html : 'More'
21414                                 }
21415
21416
21417                             ]
21418                         },
21419                         {
21420                             cls: 'ui-resizable-handle ui-resizable-e',
21421                             html : '&nbsp;&nbsp;&nbsp'
21422                         }
21423
21424                     ]
21425                 };
21426
21427                 var ctr = _this.el.select('.fc-event-container',true).first();
21428                 var cg = ctr.createChild(cfg);
21429
21430                 var sbox = c.select('.fc-day-content',true).first().getBox();
21431                 var ebox = c.select('.fc-day-content',true).first().getBox();
21432                 //Roo.log(cg);
21433                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21434                 cg.setWidth(ebox.right - sbox.x -2);
21435
21436                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21437                 
21438             }
21439             
21440         });
21441         
21442         
21443         
21444     },
21445     
21446     onEventEnter: function (e, el,event,d) {
21447         this.fireEvent('evententer', this, el, event);
21448     },
21449     
21450     onEventLeave: function (e, el,event,d) {
21451         this.fireEvent('eventleave', this, el, event);
21452     },
21453     
21454     onEventClick: function (e, el,event,d) {
21455         this.fireEvent('eventclick', this, el, event);
21456     },
21457     
21458     onMonthChange: function () {
21459         this.store.load();
21460     },
21461     
21462     onMoreEventClick: function(e, el, more)
21463     {
21464         var _this = this;
21465         
21466         this.calpopover.placement = 'right';
21467         this.calpopover.setTitle('More');
21468         
21469         this.calpopover.setContent('');
21470         
21471         var ctr = this.calpopover.el.select('.popover-content', true).first();
21472         
21473         Roo.each(more, function(m){
21474             var cfg = {
21475                 cls : 'fc-event-hori fc-event-draggable',
21476                 html : m.title
21477             };
21478             var cg = ctr.createChild(cfg);
21479             
21480             cg.on('click', _this.onEventClick, _this, m);
21481         });
21482         
21483         this.calpopover.show(el);
21484         
21485         
21486     },
21487     
21488     onLoad: function () 
21489     {   
21490         this.calevents = [];
21491         var cal = this;
21492         
21493         if(this.store.getCount() > 0){
21494             this.store.data.each(function(d){
21495                cal.addItem({
21496                     id : d.data.id,
21497                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21498                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21499                     time : d.data.start_time,
21500                     title : d.data.title,
21501                     description : d.data.description,
21502                     venue : d.data.venue
21503                 });
21504             });
21505         }
21506         
21507         this.renderEvents();
21508         
21509         if(this.calevents.length && this.loadMask){
21510             this.maskEl.hide();
21511         }
21512     },
21513     
21514     onBeforeLoad: function()
21515     {
21516         this.clearEvents();
21517         if(this.loadMask){
21518             this.maskEl.show();
21519         }
21520     }
21521 });
21522
21523  
21524  /*
21525  * - LGPL
21526  *
21527  * element
21528  * 
21529  */
21530
21531 /**
21532  * @class Roo.bootstrap.Popover
21533  * @extends Roo.bootstrap.Component
21534  * @parent none builder
21535  * @children Roo.bootstrap.Component
21536  * Bootstrap Popover class
21537  * @cfg {String} html contents of the popover   (or false to use children..)
21538  * @cfg {String} title of popover (or false to hide)
21539  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21540  * @cfg {String} trigger click || hover (or false to trigger manually)
21541  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21542  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21543  *      - if false and it has a 'parent' then it will be automatically added to that element
21544  *      - if string - Roo.get  will be called 
21545  * @cfg {Number} delay - delay before showing
21546  
21547  * @constructor
21548  * Create a new Popover
21549  * @param {Object} config The config object
21550  */
21551
21552 Roo.bootstrap.Popover = function(config){
21553     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21554     
21555     this.addEvents({
21556         // raw events
21557          /**
21558          * @event show
21559          * After the popover show
21560          * 
21561          * @param {Roo.bootstrap.Popover} this
21562          */
21563         "show" : true,
21564         /**
21565          * @event hide
21566          * After the popover hide
21567          * 
21568          * @param {Roo.bootstrap.Popover} this
21569          */
21570         "hide" : true
21571     });
21572 };
21573
21574 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21575     
21576     title: false,
21577     html: false,
21578     
21579     placement : 'right',
21580     trigger : 'hover', // hover
21581     modal : false,
21582     delay : 0,
21583     
21584     over: false,
21585     
21586     can_build_overlaid : false,
21587     
21588     maskEl : false, // the mask element
21589     headerEl : false,
21590     contentEl : false,
21591     alignEl : false, // when show is called with an element - this get's stored.
21592     
21593     getChildContainer : function()
21594     {
21595         return this.contentEl;
21596         
21597     },
21598     getPopoverHeader : function()
21599     {
21600         this.title = true; // flag not to hide it..
21601         this.headerEl.addClass('p-0');
21602         return this.headerEl
21603     },
21604     
21605     
21606     getAutoCreate : function(){
21607          
21608         var cfg = {
21609            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21610            style: 'display:block',
21611            cn : [
21612                 {
21613                     cls : 'arrow'
21614                 },
21615                 {
21616                     cls : 'popover-inner ',
21617                     cn : [
21618                         {
21619                             tag: 'h3',
21620                             cls: 'popover-title popover-header',
21621                             html : this.title === false ? '' : this.title
21622                         },
21623                         {
21624                             cls : 'popover-content popover-body '  + (this.cls || ''),
21625                             html : this.html || ''
21626                         }
21627                     ]
21628                     
21629                 }
21630            ]
21631         };
21632         
21633         return cfg;
21634     },
21635     /**
21636      * @param {string} the title
21637      */
21638     setTitle: function(str)
21639     {
21640         this.title = str;
21641         if (this.el) {
21642             this.headerEl.dom.innerHTML = str;
21643         }
21644         
21645     },
21646     /**
21647      * @param {string} the body content
21648      */
21649     setContent: function(str)
21650     {
21651         this.html = str;
21652         if (this.contentEl) {
21653             this.contentEl.dom.innerHTML = str;
21654         }
21655         
21656     },
21657     // as it get's added to the bottom of the page.
21658     onRender : function(ct, position)
21659     {
21660         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21661         
21662         
21663         
21664         if(!this.el){
21665             var cfg = Roo.apply({},  this.getAutoCreate());
21666             cfg.id = Roo.id();
21667             
21668             if (this.cls) {
21669                 cfg.cls += ' ' + this.cls;
21670             }
21671             if (this.style) {
21672                 cfg.style = this.style;
21673             }
21674             //Roo.log("adding to ");
21675             this.el = Roo.get(document.body).createChild(cfg, position);
21676 //            Roo.log(this.el);
21677         }
21678         
21679         this.contentEl = this.el.select('.popover-content',true).first();
21680         this.headerEl =  this.el.select('.popover-title',true).first();
21681         
21682         var nitems = [];
21683         if(typeof(this.items) != 'undefined'){
21684             var items = this.items;
21685             delete this.items;
21686
21687             for(var i =0;i < items.length;i++) {
21688                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21689             }
21690         }
21691
21692         this.items = nitems;
21693         
21694         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21695         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21696         
21697         
21698         
21699         this.initEvents();
21700     },
21701     
21702     resizeMask : function()
21703     {
21704         this.maskEl.setSize(
21705             Roo.lib.Dom.getViewWidth(true),
21706             Roo.lib.Dom.getViewHeight(true)
21707         );
21708     },
21709     
21710     initEvents : function()
21711     {
21712         
21713         if (!this.modal) { 
21714             Roo.bootstrap.Popover.register(this);
21715         }
21716          
21717         this.arrowEl = this.el.select('.arrow',true).first();
21718         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21719         this.el.enableDisplayMode('block');
21720         this.el.hide();
21721  
21722         
21723         if (this.over === false && !this.parent()) {
21724             return; 
21725         }
21726         if (this.triggers === false) {
21727             return;
21728         }
21729          
21730         // support parent
21731         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21732         var triggers = this.trigger ? this.trigger.split(' ') : [];
21733         Roo.each(triggers, function(trigger) {
21734         
21735             if (trigger == 'click') {
21736                 on_el.on('click', this.toggle, this);
21737             } else if (trigger != 'manual') {
21738                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21739                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21740       
21741                 on_el.on(eventIn  ,this.enter, this);
21742                 on_el.on(eventOut, this.leave, this);
21743             }
21744         }, this);
21745     },
21746     
21747     
21748     // private
21749     timeout : null,
21750     hoverState : null,
21751     
21752     toggle : function () {
21753         this.hoverState == 'in' ? this.leave() : this.enter();
21754     },
21755     
21756     enter : function () {
21757         
21758         clearTimeout(this.timeout);
21759     
21760         this.hoverState = 'in';
21761     
21762         if (!this.delay || !this.delay.show) {
21763             this.show();
21764             return;
21765         }
21766         var _t = this;
21767         this.timeout = setTimeout(function () {
21768             if (_t.hoverState == 'in') {
21769                 _t.show();
21770             }
21771         }, this.delay.show)
21772     },
21773     
21774     leave : function() {
21775         clearTimeout(this.timeout);
21776     
21777         this.hoverState = 'out';
21778     
21779         if (!this.delay || !this.delay.hide) {
21780             this.hide();
21781             return;
21782         }
21783         var _t = this;
21784         this.timeout = setTimeout(function () {
21785             if (_t.hoverState == 'out') {
21786                 _t.hide();
21787             }
21788         }, this.delay.hide)
21789     },
21790     
21791     /**
21792      * update the position of the dialog
21793      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21794      * 
21795      *
21796      */
21797     
21798     doAlign : function()
21799     {
21800         
21801         if (this.alignEl) {
21802             this.updatePosition(this.placement, true);
21803              
21804         } else {
21805             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21806             var es = this.el.getSize();
21807             var x = Roo.lib.Dom.getViewWidth()/2;
21808             var y = Roo.lib.Dom.getViewHeight()/2;
21809             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21810             
21811         }
21812
21813          
21814          
21815         
21816         
21817     },
21818     
21819     /**
21820      * Show the popover
21821      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21822      * @param {string} (left|right|top|bottom) position
21823      */
21824     show : function (on_el, placement)
21825     {
21826         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21827         on_el = on_el || false; // default to false
21828          
21829         if (!on_el) {
21830             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21831                 on_el = this.parent().el;
21832             } else if (this.over) {
21833                 on_el = Roo.get(this.over);
21834             }
21835             
21836         }
21837         
21838         this.alignEl = Roo.get( on_el );
21839
21840         if (!this.el) {
21841             this.render(document.body);
21842         }
21843         
21844         
21845          
21846         
21847         if (this.title === false) {
21848             this.headerEl.hide();
21849         }
21850         
21851        
21852         this.el.show();
21853         this.el.dom.style.display = 'block';
21854          
21855         this.doAlign();
21856         
21857         //var arrow = this.el.select('.arrow',true).first();
21858         //arrow.set(align[2], 
21859         
21860         this.el.addClass('in');
21861         
21862          
21863         
21864         this.hoverState = 'in';
21865         
21866         if (this.modal) {
21867             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21868             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21869             this.maskEl.dom.style.display = 'block';
21870             this.maskEl.addClass('show');
21871         }
21872         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21873  
21874         this.fireEvent('show', this);
21875         
21876     },
21877     /**
21878      * fire this manually after loading a grid in the table for example
21879      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21880      * @param {Boolean} try and move it if we cant get right position.
21881      */
21882     updatePosition : function(placement, try_move)
21883     {
21884         // allow for calling with no parameters
21885         placement = placement   ? placement :  this.placement;
21886         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21887         
21888         this.el.removeClass([
21889             'fade','top','bottom', 'left', 'right','in',
21890             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21891         ]);
21892         this.el.addClass(placement + ' bs-popover-' + placement);
21893         
21894         if (!this.alignEl ) {
21895             return false;
21896         }
21897         
21898         switch (placement) {
21899             case 'right':
21900                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21901                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21902                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21903                     //normal display... or moved up/down.
21904                     this.el.setXY(offset);
21905                     var xy = this.alignEl.getAnchorXY('tr', false);
21906                     xy[0]+=2;xy[1]+=5;
21907                     this.arrowEl.setXY(xy);
21908                     return true;
21909                 }
21910                 // continue through...
21911                 return this.updatePosition('left', false);
21912                 
21913             
21914             case 'left':
21915                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21916                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21917                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21918                     //normal display... or moved up/down.
21919                     this.el.setXY(offset);
21920                     var xy = this.alignEl.getAnchorXY('tl', false);
21921                     xy[0]-=10;xy[1]+=5; // << fix me
21922                     this.arrowEl.setXY(xy);
21923                     return true;
21924                 }
21925                 // call self...
21926                 return this.updatePosition('right', false);
21927             
21928             case 'top':
21929                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21930                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21931                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21932                     //normal display... or moved up/down.
21933                     this.el.setXY(offset);
21934                     var xy = this.alignEl.getAnchorXY('t', false);
21935                     xy[1]-=10; // << fix me
21936                     this.arrowEl.setXY(xy);
21937                     return true;
21938                 }
21939                 // fall through
21940                return this.updatePosition('bottom', false);
21941             
21942             case 'bottom':
21943                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21944                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21945                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21946                     //normal display... or moved up/down.
21947                     this.el.setXY(offset);
21948                     var xy = this.alignEl.getAnchorXY('b', false);
21949                      xy[1]+=2; // << fix me
21950                     this.arrowEl.setXY(xy);
21951                     return true;
21952                 }
21953                 // fall through
21954                 return this.updatePosition('top', false);
21955                 
21956             
21957         }
21958         
21959         
21960         return false;
21961     },
21962     
21963     hide : function()
21964     {
21965         this.el.setXY([0,0]);
21966         this.el.removeClass('in');
21967         this.el.hide();
21968         this.hoverState = null;
21969         this.maskEl.hide(); // always..
21970         this.fireEvent('hide', this);
21971     }
21972     
21973 });
21974
21975
21976 Roo.apply(Roo.bootstrap.Popover, {
21977
21978     alignment : {
21979         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21980         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21981         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21982         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21983     },
21984     
21985     zIndex : 20001,
21986
21987     clickHander : false,
21988     
21989     
21990
21991     onMouseDown : function(e)
21992     {
21993         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21994             /// what is nothing is showing..
21995             this.hideAll();
21996         }
21997          
21998     },
21999     
22000     
22001     popups : [],
22002     
22003     register : function(popup)
22004     {
22005         if (!Roo.bootstrap.Popover.clickHandler) {
22006             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22007         }
22008         // hide other popups.
22009         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22010         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22011         this.hideAll(); //<< why?
22012         //this.popups.push(popup);
22013     },
22014     hideAll : function()
22015     {
22016         this.popups.forEach(function(p) {
22017             p.hide();
22018         });
22019     },
22020     onShow : function() {
22021         Roo.bootstrap.Popover.popups.push(this);
22022     },
22023     onHide : function() {
22024         Roo.bootstrap.Popover.popups.remove(this);
22025     } 
22026
22027 });
22028 /**
22029  * @class Roo.bootstrap.PopoverNav
22030  * @extends Roo.bootstrap.nav.Simplebar
22031  * @parent Roo.bootstrap.Popover
22032  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22033  * @licence LGPL
22034  * Bootstrap Popover header navigation class
22035  * FIXME? should this go under nav?
22036  *
22037  * 
22038  * @constructor
22039  * Create a new Popover Header Navigation 
22040  * @param {Object} config The config object
22041  */
22042
22043 Roo.bootstrap.PopoverNav = function(config){
22044     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22045 };
22046
22047 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22048     
22049     
22050     container_method : 'getPopoverHeader' 
22051     
22052      
22053     
22054     
22055    
22056 });
22057
22058  
22059
22060  /*
22061  * - LGPL
22062  *
22063  * Progress
22064  * 
22065  */
22066
22067 /**
22068  * @class Roo.bootstrap.Progress
22069  * @extends Roo.bootstrap.Component
22070  * @children Roo.bootstrap.ProgressBar
22071  * Bootstrap Progress class
22072  * @cfg {Boolean} striped striped of the progress bar
22073  * @cfg {Boolean} active animated of the progress bar
22074  * 
22075  * 
22076  * @constructor
22077  * Create a new Progress
22078  * @param {Object} config The config object
22079  */
22080
22081 Roo.bootstrap.Progress = function(config){
22082     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22083 };
22084
22085 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22086     
22087     striped : false,
22088     active: false,
22089     
22090     getAutoCreate : function(){
22091         var cfg = {
22092             tag: 'div',
22093             cls: 'progress'
22094         };
22095         
22096         
22097         if(this.striped){
22098             cfg.cls += ' progress-striped';
22099         }
22100       
22101         if(this.active){
22102             cfg.cls += ' active';
22103         }
22104         
22105         
22106         return cfg;
22107     }
22108    
22109 });
22110
22111  
22112
22113  /*
22114  * - LGPL
22115  *
22116  * ProgressBar
22117  * 
22118  */
22119
22120 /**
22121  * @class Roo.bootstrap.ProgressBar
22122  * @extends Roo.bootstrap.Component
22123  * Bootstrap ProgressBar class
22124  * @cfg {Number} aria_valuenow aria-value now
22125  * @cfg {Number} aria_valuemin aria-value min
22126  * @cfg {Number} aria_valuemax aria-value max
22127  * @cfg {String} label label for the progress bar
22128  * @cfg {String} panel (success | info | warning | danger )
22129  * @cfg {String} role role of the progress bar
22130  * @cfg {String} sr_only text
22131  * 
22132  * 
22133  * @constructor
22134  * Create a new ProgressBar
22135  * @param {Object} config The config object
22136  */
22137
22138 Roo.bootstrap.ProgressBar = function(config){
22139     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22140 };
22141
22142 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22143     
22144     aria_valuenow : 0,
22145     aria_valuemin : 0,
22146     aria_valuemax : 100,
22147     label : false,
22148     panel : false,
22149     role : false,
22150     sr_only: false,
22151     
22152     getAutoCreate : function()
22153     {
22154         
22155         var cfg = {
22156             tag: 'div',
22157             cls: 'progress-bar',
22158             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22159         };
22160         
22161         if(this.sr_only){
22162             cfg.cn = {
22163                 tag: 'span',
22164                 cls: 'sr-only',
22165                 html: this.sr_only
22166             }
22167         }
22168         
22169         if(this.role){
22170             cfg.role = this.role;
22171         }
22172         
22173         if(this.aria_valuenow){
22174             cfg['aria-valuenow'] = this.aria_valuenow;
22175         }
22176         
22177         if(this.aria_valuemin){
22178             cfg['aria-valuemin'] = this.aria_valuemin;
22179         }
22180         
22181         if(this.aria_valuemax){
22182             cfg['aria-valuemax'] = this.aria_valuemax;
22183         }
22184         
22185         if(this.label && !this.sr_only){
22186             cfg.html = this.label;
22187         }
22188         
22189         if(this.panel){
22190             cfg.cls += ' progress-bar-' + this.panel;
22191         }
22192         
22193         return cfg;
22194     },
22195     
22196     update : function(aria_valuenow)
22197     {
22198         this.aria_valuenow = aria_valuenow;
22199         
22200         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22201     }
22202    
22203 });
22204
22205  
22206
22207  /**
22208  * @class Roo.bootstrap.TabGroup
22209  * @extends Roo.bootstrap.Column
22210  * @children Roo.bootstrap.TabPanel
22211  * Bootstrap Column class
22212  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22213  * @cfg {Boolean} carousel true to make the group behave like a carousel
22214  * @cfg {Boolean} bullets show bullets for the panels
22215  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22216  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22217  * @cfg {Boolean} showarrow (true|false) show arrow default true
22218  * 
22219  * @constructor
22220  * Create a new TabGroup
22221  * @param {Object} config The config object
22222  */
22223
22224 Roo.bootstrap.TabGroup = function(config){
22225     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22226     if (!this.navId) {
22227         this.navId = Roo.id();
22228     }
22229     this.tabs = [];
22230     Roo.bootstrap.TabGroup.register(this);
22231     
22232 };
22233
22234 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22235     
22236     carousel : false,
22237     transition : false,
22238     bullets : 0,
22239     timer : 0,
22240     autoslide : false,
22241     slideFn : false,
22242     slideOnTouch : false,
22243     showarrow : true,
22244     
22245     getAutoCreate : function()
22246     {
22247         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22248         
22249         cfg.cls += ' tab-content';
22250         
22251         if (this.carousel) {
22252             cfg.cls += ' carousel slide';
22253             
22254             cfg.cn = [{
22255                cls : 'carousel-inner',
22256                cn : []
22257             }];
22258         
22259             if(this.bullets  && !Roo.isTouch){
22260                 
22261                 var bullets = {
22262                     cls : 'carousel-bullets',
22263                     cn : []
22264                 };
22265                
22266                 if(this.bullets_cls){
22267                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22268                 }
22269                 
22270                 bullets.cn.push({
22271                     cls : 'clear'
22272                 });
22273                 
22274                 cfg.cn[0].cn.push(bullets);
22275             }
22276             
22277             if(this.showarrow){
22278                 cfg.cn[0].cn.push({
22279                     tag : 'div',
22280                     class : 'carousel-arrow',
22281                     cn : [
22282                         {
22283                             tag : 'div',
22284                             class : 'carousel-prev',
22285                             cn : [
22286                                 {
22287                                     tag : 'i',
22288                                     class : 'fa fa-chevron-left'
22289                                 }
22290                             ]
22291                         },
22292                         {
22293                             tag : 'div',
22294                             class : 'carousel-next',
22295                             cn : [
22296                                 {
22297                                     tag : 'i',
22298                                     class : 'fa fa-chevron-right'
22299                                 }
22300                             ]
22301                         }
22302                     ]
22303                 });
22304             }
22305             
22306         }
22307         
22308         return cfg;
22309     },
22310     
22311     initEvents:  function()
22312     {
22313 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22314 //            this.el.on("touchstart", this.onTouchStart, this);
22315 //        }
22316         
22317         if(this.autoslide){
22318             var _this = this;
22319             
22320             this.slideFn = window.setInterval(function() {
22321                 _this.showPanelNext();
22322             }, this.timer);
22323         }
22324         
22325         if(this.showarrow){
22326             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22327             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22328         }
22329         
22330         
22331     },
22332     
22333 //    onTouchStart : function(e, el, o)
22334 //    {
22335 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22336 //            return;
22337 //        }
22338 //        
22339 //        this.showPanelNext();
22340 //    },
22341     
22342     
22343     getChildContainer : function()
22344     {
22345         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22346     },
22347     
22348     /**
22349     * register a Navigation item
22350     * @param {Roo.bootstrap.nav.Item} the navitem to add
22351     */
22352     register : function(item)
22353     {
22354         this.tabs.push( item);
22355         item.navId = this.navId; // not really needed..
22356         this.addBullet();
22357     
22358     },
22359     
22360     getActivePanel : function()
22361     {
22362         var r = false;
22363         Roo.each(this.tabs, function(t) {
22364             if (t.active) {
22365                 r = t;
22366                 return false;
22367             }
22368             return null;
22369         });
22370         return r;
22371         
22372     },
22373     getPanelByName : function(n)
22374     {
22375         var r = false;
22376         Roo.each(this.tabs, function(t) {
22377             if (t.tabId == n) {
22378                 r = t;
22379                 return false;
22380             }
22381             return null;
22382         });
22383         return r;
22384     },
22385     indexOfPanel : function(p)
22386     {
22387         var r = false;
22388         Roo.each(this.tabs, function(t,i) {
22389             if (t.tabId == p.tabId) {
22390                 r = i;
22391                 return false;
22392             }
22393             return null;
22394         });
22395         return r;
22396     },
22397     /**
22398      * show a specific panel
22399      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22400      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22401      */
22402     showPanel : function (pan)
22403     {
22404         if(this.transition || typeof(pan) == 'undefined'){
22405             Roo.log("waiting for the transitionend");
22406             return false;
22407         }
22408         
22409         if (typeof(pan) == 'number') {
22410             pan = this.tabs[pan];
22411         }
22412         
22413         if (typeof(pan) == 'string') {
22414             pan = this.getPanelByName(pan);
22415         }
22416         
22417         var cur = this.getActivePanel();
22418         
22419         if(!pan || !cur){
22420             Roo.log('pan or acitve pan is undefined');
22421             return false;
22422         }
22423         
22424         if (pan.tabId == this.getActivePanel().tabId) {
22425             return true;
22426         }
22427         
22428         if (false === cur.fireEvent('beforedeactivate')) {
22429             return false;
22430         }
22431         
22432         if(this.bullets > 0 && !Roo.isTouch){
22433             this.setActiveBullet(this.indexOfPanel(pan));
22434         }
22435         
22436         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22437             
22438             //class="carousel-item carousel-item-next carousel-item-left"
22439             
22440             this.transition = true;
22441             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22442             var lr = dir == 'next' ? 'left' : 'right';
22443             pan.el.addClass(dir); // or prev
22444             pan.el.addClass('carousel-item-' + dir); // or prev
22445             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22446             cur.el.addClass(lr); // or right
22447             pan.el.addClass(lr);
22448             cur.el.addClass('carousel-item-' +lr); // or right
22449             pan.el.addClass('carousel-item-' +lr);
22450             
22451             
22452             var _this = this;
22453             cur.el.on('transitionend', function() {
22454                 Roo.log("trans end?");
22455                 
22456                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22457                 pan.setActive(true);
22458                 
22459                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22460                 cur.setActive(false);
22461                 
22462                 _this.transition = false;
22463                 
22464             }, this, { single:  true } );
22465             
22466             return true;
22467         }
22468         
22469         cur.setActive(false);
22470         pan.setActive(true);
22471         
22472         return true;
22473         
22474     },
22475     showPanelNext : function()
22476     {
22477         var i = this.indexOfPanel(this.getActivePanel());
22478         
22479         if (i >= this.tabs.length - 1 && !this.autoslide) {
22480             return;
22481         }
22482         
22483         if (i >= this.tabs.length - 1 && this.autoslide) {
22484             i = -1;
22485         }
22486         
22487         this.showPanel(this.tabs[i+1]);
22488     },
22489     
22490     showPanelPrev : function()
22491     {
22492         var i = this.indexOfPanel(this.getActivePanel());
22493         
22494         if (i  < 1 && !this.autoslide) {
22495             return;
22496         }
22497         
22498         if (i < 1 && this.autoslide) {
22499             i = this.tabs.length;
22500         }
22501         
22502         this.showPanel(this.tabs[i-1]);
22503     },
22504     
22505     
22506     addBullet: function()
22507     {
22508         if(!this.bullets || Roo.isTouch){
22509             return;
22510         }
22511         var ctr = this.el.select('.carousel-bullets',true).first();
22512         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22513         var bullet = ctr.createChild({
22514             cls : 'bullet bullet-' + i
22515         },ctr.dom.lastChild);
22516         
22517         
22518         var _this = this;
22519         
22520         bullet.on('click', (function(e, el, o, ii, t){
22521
22522             e.preventDefault();
22523
22524             this.showPanel(ii);
22525
22526             if(this.autoslide && this.slideFn){
22527                 clearInterval(this.slideFn);
22528                 this.slideFn = window.setInterval(function() {
22529                     _this.showPanelNext();
22530                 }, this.timer);
22531             }
22532
22533         }).createDelegate(this, [i, bullet], true));
22534                 
22535         
22536     },
22537      
22538     setActiveBullet : function(i)
22539     {
22540         if(Roo.isTouch){
22541             return;
22542         }
22543         
22544         Roo.each(this.el.select('.bullet', true).elements, function(el){
22545             el.removeClass('selected');
22546         });
22547
22548         var bullet = this.el.select('.bullet-' + i, true).first();
22549         
22550         if(!bullet){
22551             return;
22552         }
22553         
22554         bullet.addClass('selected');
22555     }
22556     
22557     
22558   
22559 });
22560
22561  
22562
22563  
22564  
22565 Roo.apply(Roo.bootstrap.TabGroup, {
22566     
22567     groups: {},
22568      /**
22569     * register a Navigation Group
22570     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22571     */
22572     register : function(navgrp)
22573     {
22574         this.groups[navgrp.navId] = navgrp;
22575         
22576     },
22577     /**
22578     * fetch a Navigation Group based on the navigation ID
22579     * if one does not exist , it will get created.
22580     * @param {string} the navgroup to add
22581     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22582     */
22583     get: function(navId) {
22584         if (typeof(this.groups[navId]) == 'undefined') {
22585             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22586         }
22587         return this.groups[navId] ;
22588     }
22589     
22590     
22591     
22592 });
22593
22594  /*
22595  * - LGPL
22596  *
22597  * TabPanel
22598  * 
22599  */
22600
22601 /**
22602  * @class Roo.bootstrap.TabPanel
22603  * @extends Roo.bootstrap.Component
22604  * @children Roo.bootstrap.Component
22605  * Bootstrap TabPanel class
22606  * @cfg {Boolean} active panel active
22607  * @cfg {String} html panel content
22608  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22609  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22610  * @cfg {String} href click to link..
22611  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22612  * 
22613  * 
22614  * @constructor
22615  * Create a new TabPanel
22616  * @param {Object} config The config object
22617  */
22618
22619 Roo.bootstrap.TabPanel = function(config){
22620     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22621     this.addEvents({
22622         /**
22623              * @event changed
22624              * Fires when the active status changes
22625              * @param {Roo.bootstrap.TabPanel} this
22626              * @param {Boolean} state the new state
22627             
22628          */
22629         'changed': true,
22630         /**
22631              * @event beforedeactivate
22632              * Fires before a tab is de-activated - can be used to do validation on a form.
22633              * @param {Roo.bootstrap.TabPanel} this
22634              * @return {Boolean} false if there is an error
22635             
22636          */
22637         'beforedeactivate': true
22638      });
22639     
22640     this.tabId = this.tabId || Roo.id();
22641   
22642 };
22643
22644 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22645     
22646     active: false,
22647     html: false,
22648     tabId: false,
22649     navId : false,
22650     href : '',
22651     touchSlide : false,
22652     getAutoCreate : function(){
22653         
22654         
22655         var cfg = {
22656             tag: 'div',
22657             // item is needed for carousel - not sure if it has any effect otherwise
22658             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22659             html: this.html || ''
22660         };
22661         
22662         if(this.active){
22663             cfg.cls += ' active';
22664         }
22665         
22666         if(this.tabId){
22667             cfg.tabId = this.tabId;
22668         }
22669         
22670         
22671         
22672         return cfg;
22673     },
22674     
22675     initEvents:  function()
22676     {
22677         var p = this.parent();
22678         
22679         this.navId = this.navId || p.navId;
22680         
22681         if (typeof(this.navId) != 'undefined') {
22682             // not really needed.. but just in case.. parent should be a NavGroup.
22683             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22684             
22685             tg.register(this);
22686             
22687             var i = tg.tabs.length - 1;
22688             
22689             if(this.active && tg.bullets > 0 && i < tg.bullets){
22690                 tg.setActiveBullet(i);
22691             }
22692         }
22693         
22694         this.el.on('click', this.onClick, this);
22695         
22696         if(Roo.isTouch && this.touchSlide){
22697             this.el.on("touchstart", this.onTouchStart, this);
22698             this.el.on("touchmove", this.onTouchMove, this);
22699             this.el.on("touchend", this.onTouchEnd, this);
22700         }
22701         
22702     },
22703     
22704     onRender : function(ct, position)
22705     {
22706         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22707     },
22708     
22709     setActive : function(state)
22710     {
22711         Roo.log("panel - set active " + this.tabId + "=" + state);
22712         
22713         this.active = state;
22714         if (!state) {
22715             this.el.removeClass('active');
22716             
22717         } else  if (!this.el.hasClass('active')) {
22718             this.el.addClass('active');
22719         }
22720         
22721         this.fireEvent('changed', this, state);
22722     },
22723     
22724     onClick : function(e)
22725     {
22726         e.preventDefault();
22727         
22728         if(!this.href.length){
22729             return;
22730         }
22731         
22732         window.location.href = this.href;
22733     },
22734     
22735     startX : 0,
22736     startY : 0,
22737     endX : 0,
22738     endY : 0,
22739     swiping : false,
22740     
22741     onTouchStart : function(e)
22742     {
22743         this.swiping = false;
22744         
22745         this.startX = e.browserEvent.touches[0].clientX;
22746         this.startY = e.browserEvent.touches[0].clientY;
22747     },
22748     
22749     onTouchMove : function(e)
22750     {
22751         this.swiping = true;
22752         
22753         this.endX = e.browserEvent.touches[0].clientX;
22754         this.endY = e.browserEvent.touches[0].clientY;
22755     },
22756     
22757     onTouchEnd : function(e)
22758     {
22759         if(!this.swiping){
22760             this.onClick(e);
22761             return;
22762         }
22763         
22764         var tabGroup = this.parent();
22765         
22766         if(this.endX > this.startX){ // swiping right
22767             tabGroup.showPanelPrev();
22768             return;
22769         }
22770         
22771         if(this.startX > this.endX){ // swiping left
22772             tabGroup.showPanelNext();
22773             return;
22774         }
22775     }
22776     
22777     
22778 });
22779  
22780
22781  
22782
22783  /*
22784  * - LGPL
22785  *
22786  * DateField
22787  * 
22788  */
22789
22790 /**
22791  * @class Roo.bootstrap.form.DateField
22792  * @extends Roo.bootstrap.form.Input
22793  * Bootstrap DateField class
22794  * @cfg {Number} weekStart default 0
22795  * @cfg {String} viewMode default empty, (months|years)
22796  * @cfg {String} minViewMode default empty, (months|years)
22797  * @cfg {Number} startDate default -Infinity
22798  * @cfg {Number} endDate default Infinity
22799  * @cfg {Boolean} todayHighlight default false
22800  * @cfg {Boolean} todayBtn default false
22801  * @cfg {Boolean} calendarWeeks default false
22802  * @cfg {Object} daysOfWeekDisabled default empty
22803  * @cfg {Boolean} singleMode default false (true | false)
22804  * 
22805  * @cfg {Boolean} keyboardNavigation default true
22806  * @cfg {String} language default en
22807  * 
22808  * @constructor
22809  * Create a new DateField
22810  * @param {Object} config The config object
22811  */
22812
22813 Roo.bootstrap.form.DateField = function(config){
22814     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22815      this.addEvents({
22816             /**
22817              * @event show
22818              * Fires when this field show.
22819              * @param {Roo.bootstrap.form.DateField} this
22820              * @param {Mixed} date The date value
22821              */
22822             show : true,
22823             /**
22824              * @event show
22825              * Fires when this field hide.
22826              * @param {Roo.bootstrap.form.DateField} this
22827              * @param {Mixed} date The date value
22828              */
22829             hide : true,
22830             /**
22831              * @event select
22832              * Fires when select a date.
22833              * @param {Roo.bootstrap.form.DateField} this
22834              * @param {Mixed} date The date value
22835              */
22836             select : true,
22837             /**
22838              * @event beforeselect
22839              * Fires when before select a date.
22840              * @param {Roo.bootstrap.form.DateField} this
22841              * @param {Mixed} date The date value
22842              */
22843             beforeselect : true
22844         });
22845 };
22846
22847 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22848     
22849     /**
22850      * @cfg {String} format
22851      * The default date format string which can be overriden for localization support.  The format must be
22852      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22853      */
22854     format : "m/d/y",
22855     /**
22856      * @cfg {String} altFormats
22857      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22858      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22859      */
22860     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22861     
22862     weekStart : 0,
22863     
22864     viewMode : '',
22865     
22866     minViewMode : '',
22867     
22868     todayHighlight : false,
22869     
22870     todayBtn: false,
22871     
22872     language: 'en',
22873     
22874     keyboardNavigation: true,
22875     
22876     calendarWeeks: false,
22877     
22878     startDate: -Infinity,
22879     
22880     endDate: Infinity,
22881     
22882     daysOfWeekDisabled: [],
22883     
22884     _events: [],
22885     
22886     singleMode : false,
22887     
22888     UTCDate: function()
22889     {
22890         return new Date(Date.UTC.apply(Date, arguments));
22891     },
22892     
22893     UTCToday: function()
22894     {
22895         var today = new Date();
22896         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22897     },
22898     
22899     getDate: function() {
22900             var d = this.getUTCDate();
22901             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22902     },
22903     
22904     getUTCDate: function() {
22905             return this.date;
22906     },
22907     
22908     setDate: function(d) {
22909             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22910     },
22911     
22912     setUTCDate: function(d) {
22913             this.date = d;
22914             this.setValue(this.formatDate(this.date));
22915     },
22916         
22917     onRender: function(ct, position)
22918     {
22919         
22920         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22921         
22922         this.language = this.language || 'en';
22923         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22924         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22925         
22926         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22927         this.format = this.format || 'm/d/y';
22928         this.isInline = false;
22929         this.isInput = true;
22930         this.component = this.el.select('.add-on', true).first() || false;
22931         this.component = (this.component && this.component.length === 0) ? false : this.component;
22932         this.hasInput = this.component && this.inputEl().length;
22933         
22934         if (typeof(this.minViewMode === 'string')) {
22935             switch (this.minViewMode) {
22936                 case 'months':
22937                     this.minViewMode = 1;
22938                     break;
22939                 case 'years':
22940                     this.minViewMode = 2;
22941                     break;
22942                 default:
22943                     this.minViewMode = 0;
22944                     break;
22945             }
22946         }
22947         
22948         if (typeof(this.viewMode === 'string')) {
22949             switch (this.viewMode) {
22950                 case 'months':
22951                     this.viewMode = 1;
22952                     break;
22953                 case 'years':
22954                     this.viewMode = 2;
22955                     break;
22956                 default:
22957                     this.viewMode = 0;
22958                     break;
22959             }
22960         }
22961                 
22962         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22963         
22964 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22965         
22966         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22967         
22968         this.picker().on('mousedown', this.onMousedown, this);
22969         this.picker().on('click', this.onClick, this);
22970         
22971         this.picker().addClass('datepicker-dropdown');
22972         
22973         this.startViewMode = this.viewMode;
22974         
22975         if(this.singleMode){
22976             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22977                 v.setVisibilityMode(Roo.Element.DISPLAY);
22978                 v.hide();
22979             });
22980             
22981             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22982                 v.setStyle('width', '189px');
22983             });
22984         }
22985         
22986         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22987             if(!this.calendarWeeks){
22988                 v.remove();
22989                 return;
22990             }
22991             
22992             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22993             v.attr('colspan', function(i, val){
22994                 return parseInt(val) + 1;
22995             });
22996         });
22997                         
22998         
22999         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23000         
23001         this.setStartDate(this.startDate);
23002         this.setEndDate(this.endDate);
23003         
23004         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23005         
23006         this.fillDow();
23007         this.fillMonths();
23008         this.update();
23009         this.showMode();
23010         
23011         if(this.isInline) {
23012             this.showPopup();
23013         }
23014     },
23015     
23016     picker : function()
23017     {
23018         return this.pickerEl;
23019 //        return this.el.select('.datepicker', true).first();
23020     },
23021     
23022     fillDow: function()
23023     {
23024         var dowCnt = this.weekStart;
23025         
23026         var dow = {
23027             tag: 'tr',
23028             cn: [
23029                 
23030             ]
23031         };
23032         
23033         if(this.calendarWeeks){
23034             dow.cn.push({
23035                 tag: 'th',
23036                 cls: 'cw',
23037                 html: '&nbsp;'
23038             })
23039         }
23040         
23041         while (dowCnt < this.weekStart + 7) {
23042             dow.cn.push({
23043                 tag: 'th',
23044                 cls: 'dow',
23045                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23046             });
23047         }
23048         
23049         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23050     },
23051     
23052     fillMonths: function()
23053     {    
23054         var i = 0;
23055         var months = this.picker().select('>.datepicker-months td', true).first();
23056         
23057         months.dom.innerHTML = '';
23058         
23059         while (i < 12) {
23060             var month = {
23061                 tag: 'span',
23062                 cls: 'month',
23063                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23064             };
23065             
23066             months.createChild(month);
23067         }
23068         
23069     },
23070     
23071     update: function()
23072     {
23073         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;
23074         
23075         if (this.date < this.startDate) {
23076             this.viewDate = new Date(this.startDate);
23077         } else if (this.date > this.endDate) {
23078             this.viewDate = new Date(this.endDate);
23079         } else {
23080             this.viewDate = new Date(this.date);
23081         }
23082         
23083         this.fill();
23084     },
23085     
23086     fill: function() 
23087     {
23088         var d = new Date(this.viewDate),
23089                 year = d.getUTCFullYear(),
23090                 month = d.getUTCMonth(),
23091                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23092                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23093                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23094                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23095                 currentDate = this.date && this.date.valueOf(),
23096                 today = this.UTCToday();
23097         
23098         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23099         
23100 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23101         
23102 //        this.picker.select('>tfoot th.today').
23103 //                                              .text(dates[this.language].today)
23104 //                                              .toggle(this.todayBtn !== false);
23105     
23106         this.updateNavArrows();
23107         this.fillMonths();
23108                                                 
23109         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23110         
23111         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23112          
23113         prevMonth.setUTCDate(day);
23114         
23115         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23116         
23117         var nextMonth = new Date(prevMonth);
23118         
23119         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23120         
23121         nextMonth = nextMonth.valueOf();
23122         
23123         var fillMonths = false;
23124         
23125         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23126         
23127         while(prevMonth.valueOf() <= nextMonth) {
23128             var clsName = '';
23129             
23130             if (prevMonth.getUTCDay() === this.weekStart) {
23131                 if(fillMonths){
23132                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23133                 }
23134                     
23135                 fillMonths = {
23136                     tag: 'tr',
23137                     cn: []
23138                 };
23139                 
23140                 if(this.calendarWeeks){
23141                     // ISO 8601: First week contains first thursday.
23142                     // ISO also states week starts on Monday, but we can be more abstract here.
23143                     var
23144                     // Start of current week: based on weekstart/current date
23145                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23146                     // Thursday of this week
23147                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23148                     // First Thursday of year, year from thursday
23149                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23150                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23151                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23152                     
23153                     fillMonths.cn.push({
23154                         tag: 'td',
23155                         cls: 'cw',
23156                         html: calWeek
23157                     });
23158                 }
23159             }
23160             
23161             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23162                 clsName += ' old';
23163             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23164                 clsName += ' new';
23165             }
23166             if (this.todayHighlight &&
23167                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23168                 prevMonth.getUTCMonth() == today.getMonth() &&
23169                 prevMonth.getUTCDate() == today.getDate()) {
23170                 clsName += ' today';
23171             }
23172             
23173             if (currentDate && prevMonth.valueOf() === currentDate) {
23174                 clsName += ' active';
23175             }
23176             
23177             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23178                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23179                     clsName += ' disabled';
23180             }
23181             
23182             fillMonths.cn.push({
23183                 tag: 'td',
23184                 cls: 'day ' + clsName,
23185                 html: prevMonth.getDate()
23186             });
23187             
23188             prevMonth.setDate(prevMonth.getDate()+1);
23189         }
23190           
23191         var currentYear = this.date && this.date.getUTCFullYear();
23192         var currentMonth = this.date && this.date.getUTCMonth();
23193         
23194         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23195         
23196         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23197             v.removeClass('active');
23198             
23199             if(currentYear === year && k === currentMonth){
23200                 v.addClass('active');
23201             }
23202             
23203             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23204                 v.addClass('disabled');
23205             }
23206             
23207         });
23208         
23209         
23210         year = parseInt(year/10, 10) * 10;
23211         
23212         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23213         
23214         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23215         
23216         year -= 1;
23217         for (var i = -1; i < 11; i++) {
23218             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23219                 tag: 'span',
23220                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23221                 html: year
23222             });
23223             
23224             year += 1;
23225         }
23226     },
23227     
23228     showMode: function(dir) 
23229     {
23230         if (dir) {
23231             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23232         }
23233         
23234         Roo.each(this.picker().select('>div',true).elements, function(v){
23235             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23236             v.hide();
23237         });
23238         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23239     },
23240     
23241     place: function()
23242     {
23243         if(this.isInline) {
23244             return;
23245         }
23246         
23247         this.picker().removeClass(['bottom', 'top']);
23248         
23249         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23250             /*
23251              * place to the top of element!
23252              *
23253              */
23254             
23255             this.picker().addClass('top');
23256             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23257             
23258             return;
23259         }
23260         
23261         this.picker().addClass('bottom');
23262         
23263         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23264     },
23265     
23266     parseDate : function(value)
23267     {
23268         if(!value || value instanceof Date){
23269             return value;
23270         }
23271         var v = Date.parseDate(value, this.format);
23272         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23273             v = Date.parseDate(value, 'Y-m-d');
23274         }
23275         if(!v && this.altFormats){
23276             if(!this.altFormatsArray){
23277                 this.altFormatsArray = this.altFormats.split("|");
23278             }
23279             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23280                 v = Date.parseDate(value, this.altFormatsArray[i]);
23281             }
23282         }
23283         return v;
23284     },
23285     
23286     formatDate : function(date, fmt)
23287     {   
23288         return (!date || !(date instanceof Date)) ?
23289         date : date.dateFormat(fmt || this.format);
23290     },
23291     
23292     onFocus : function()
23293     {
23294         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23295         this.showPopup();
23296     },
23297     
23298     onBlur : function()
23299     {
23300         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23301         
23302         var d = this.inputEl().getValue();
23303         
23304         this.setValue(d);
23305                 
23306         this.hidePopup();
23307     },
23308     
23309     showPopup : function()
23310     {
23311         this.picker().show();
23312         this.update();
23313         this.place();
23314         
23315         this.fireEvent('showpopup', this, this.date);
23316     },
23317     
23318     hidePopup : function()
23319     {
23320         if(this.isInline) {
23321             return;
23322         }
23323         this.picker().hide();
23324         this.viewMode = this.startViewMode;
23325         this.showMode();
23326         
23327         this.fireEvent('hidepopup', this, this.date);
23328         
23329     },
23330     
23331     onMousedown: function(e)
23332     {
23333         e.stopPropagation();
23334         e.preventDefault();
23335     },
23336     
23337     keyup: function(e)
23338     {
23339         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23340         this.update();
23341     },
23342
23343     setValue: function(v)
23344     {
23345         if(this.fireEvent('beforeselect', this, v) !== false){
23346             var d = new Date(this.parseDate(v) ).clearTime();
23347         
23348             if(isNaN(d.getTime())){
23349                 this.date = this.viewDate = '';
23350                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23351                 return;
23352             }
23353
23354             v = this.formatDate(d);
23355
23356             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23357
23358             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23359
23360             this.update();
23361
23362             this.fireEvent('select', this, this.date);
23363         }
23364     },
23365     
23366     getValue: function()
23367     {
23368         return this.formatDate(this.date);
23369     },
23370     
23371     fireKey: function(e)
23372     {
23373         if (!this.picker().isVisible()){
23374             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23375                 this.showPopup();
23376             }
23377             return;
23378         }
23379         
23380         var dateChanged = false,
23381         dir, day, month,
23382         newDate, newViewDate;
23383         
23384         switch(e.keyCode){
23385             case 27: // escape
23386                 this.hidePopup();
23387                 e.preventDefault();
23388                 break;
23389             case 37: // left
23390             case 39: // right
23391                 if (!this.keyboardNavigation) {
23392                     break;
23393                 }
23394                 dir = e.keyCode == 37 ? -1 : 1;
23395                 
23396                 if (e.ctrlKey){
23397                     newDate = this.moveYear(this.date, dir);
23398                     newViewDate = this.moveYear(this.viewDate, dir);
23399                 } else if (e.shiftKey){
23400                     newDate = this.moveMonth(this.date, dir);
23401                     newViewDate = this.moveMonth(this.viewDate, dir);
23402                 } else {
23403                     newDate = new Date(this.date);
23404                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23405                     newViewDate = new Date(this.viewDate);
23406                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23407                 }
23408                 if (this.dateWithinRange(newDate)){
23409                     this.date = newDate;
23410                     this.viewDate = newViewDate;
23411                     this.setValue(this.formatDate(this.date));
23412 //                    this.update();
23413                     e.preventDefault();
23414                     dateChanged = true;
23415                 }
23416                 break;
23417             case 38: // up
23418             case 40: // down
23419                 if (!this.keyboardNavigation) {
23420                     break;
23421                 }
23422                 dir = e.keyCode == 38 ? -1 : 1;
23423                 if (e.ctrlKey){
23424                     newDate = this.moveYear(this.date, dir);
23425                     newViewDate = this.moveYear(this.viewDate, dir);
23426                 } else if (e.shiftKey){
23427                     newDate = this.moveMonth(this.date, dir);
23428                     newViewDate = this.moveMonth(this.viewDate, dir);
23429                 } else {
23430                     newDate = new Date(this.date);
23431                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23432                     newViewDate = new Date(this.viewDate);
23433                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23434                 }
23435                 if (this.dateWithinRange(newDate)){
23436                     this.date = newDate;
23437                     this.viewDate = newViewDate;
23438                     this.setValue(this.formatDate(this.date));
23439 //                    this.update();
23440                     e.preventDefault();
23441                     dateChanged = true;
23442                 }
23443                 break;
23444             case 13: // enter
23445                 this.setValue(this.formatDate(this.date));
23446                 this.hidePopup();
23447                 e.preventDefault();
23448                 break;
23449             case 9: // tab
23450                 this.setValue(this.formatDate(this.date));
23451                 this.hidePopup();
23452                 break;
23453             case 16: // shift
23454             case 17: // ctrl
23455             case 18: // alt
23456                 break;
23457             default :
23458                 this.hidePopup();
23459                 
23460         }
23461     },
23462     
23463     
23464     onClick: function(e) 
23465     {
23466         e.stopPropagation();
23467         e.preventDefault();
23468         
23469         var target = e.getTarget();
23470         
23471         if(target.nodeName.toLowerCase() === 'i'){
23472             target = Roo.get(target).dom.parentNode;
23473         }
23474         
23475         var nodeName = target.nodeName;
23476         var className = target.className;
23477         var html = target.innerHTML;
23478         //Roo.log(nodeName);
23479         
23480         switch(nodeName.toLowerCase()) {
23481             case 'th':
23482                 switch(className) {
23483                     case 'switch':
23484                         this.showMode(1);
23485                         break;
23486                     case 'prev':
23487                     case 'next':
23488                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23489                         switch(this.viewMode){
23490                                 case 0:
23491                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23492                                         break;
23493                                 case 1:
23494                                 case 2:
23495                                         this.viewDate = this.moveYear(this.viewDate, dir);
23496                                         break;
23497                         }
23498                         this.fill();
23499                         break;
23500                     case 'today':
23501                         var date = new Date();
23502                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23503 //                        this.fill()
23504                         this.setValue(this.formatDate(this.date));
23505                         
23506                         this.hidePopup();
23507                         break;
23508                 }
23509                 break;
23510             case 'span':
23511                 if (className.indexOf('disabled') < 0) {
23512                 if (!this.viewDate) {
23513                     this.viewDate = new Date();
23514                 }
23515                 this.viewDate.setUTCDate(1);
23516                     if (className.indexOf('month') > -1) {
23517                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23518                     } else {
23519                         var year = parseInt(html, 10) || 0;
23520                         this.viewDate.setUTCFullYear(year);
23521                         
23522                     }
23523                     
23524                     if(this.singleMode){
23525                         this.setValue(this.formatDate(this.viewDate));
23526                         this.hidePopup();
23527                         return;
23528                     }
23529                     
23530                     this.showMode(-1);
23531                     this.fill();
23532                 }
23533                 break;
23534                 
23535             case 'td':
23536                 //Roo.log(className);
23537                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23538                     var day = parseInt(html, 10) || 1;
23539                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23540                         month = (this.viewDate || new Date()).getUTCMonth();
23541
23542                     if (className.indexOf('old') > -1) {
23543                         if(month === 0 ){
23544                             month = 11;
23545                             year -= 1;
23546                         }else{
23547                             month -= 1;
23548                         }
23549                     } else if (className.indexOf('new') > -1) {
23550                         if (month == 11) {
23551                             month = 0;
23552                             year += 1;
23553                         } else {
23554                             month += 1;
23555                         }
23556                     }
23557                     //Roo.log([year,month,day]);
23558                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23559                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23560 //                    this.fill();
23561                     //Roo.log(this.formatDate(this.date));
23562                     this.setValue(this.formatDate(this.date));
23563                     this.hidePopup();
23564                 }
23565                 break;
23566         }
23567     },
23568     
23569     setStartDate: function(startDate)
23570     {
23571         this.startDate = startDate || -Infinity;
23572         if (this.startDate !== -Infinity) {
23573             this.startDate = this.parseDate(this.startDate);
23574         }
23575         this.update();
23576         this.updateNavArrows();
23577     },
23578
23579     setEndDate: function(endDate)
23580     {
23581         this.endDate = endDate || Infinity;
23582         if (this.endDate !== Infinity) {
23583             this.endDate = this.parseDate(this.endDate);
23584         }
23585         this.update();
23586         this.updateNavArrows();
23587     },
23588     
23589     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23590     {
23591         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23592         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23593             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23594         }
23595         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23596             return parseInt(d, 10);
23597         });
23598         this.update();
23599         this.updateNavArrows();
23600     },
23601     
23602     updateNavArrows: function() 
23603     {
23604         if(this.singleMode){
23605             return;
23606         }
23607         
23608         var d = new Date(this.viewDate),
23609         year = d.getUTCFullYear(),
23610         month = d.getUTCMonth();
23611         
23612         Roo.each(this.picker().select('.prev', true).elements, function(v){
23613             v.show();
23614             switch (this.viewMode) {
23615                 case 0:
23616
23617                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23618                         v.hide();
23619                     }
23620                     break;
23621                 case 1:
23622                 case 2:
23623                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23624                         v.hide();
23625                     }
23626                     break;
23627             }
23628         });
23629         
23630         Roo.each(this.picker().select('.next', true).elements, function(v){
23631             v.show();
23632             switch (this.viewMode) {
23633                 case 0:
23634
23635                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23636                         v.hide();
23637                     }
23638                     break;
23639                 case 1:
23640                 case 2:
23641                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23642                         v.hide();
23643                     }
23644                     break;
23645             }
23646         })
23647     },
23648     
23649     moveMonth: function(date, dir)
23650     {
23651         if (!dir) {
23652             return date;
23653         }
23654         var new_date = new Date(date.valueOf()),
23655         day = new_date.getUTCDate(),
23656         month = new_date.getUTCMonth(),
23657         mag = Math.abs(dir),
23658         new_month, test;
23659         dir = dir > 0 ? 1 : -1;
23660         if (mag == 1){
23661             test = dir == -1
23662             // If going back one month, make sure month is not current month
23663             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23664             ? function(){
23665                 return new_date.getUTCMonth() == month;
23666             }
23667             // If going forward one month, make sure month is as expected
23668             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23669             : function(){
23670                 return new_date.getUTCMonth() != new_month;
23671             };
23672             new_month = month + dir;
23673             new_date.setUTCMonth(new_month);
23674             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23675             if (new_month < 0 || new_month > 11) {
23676                 new_month = (new_month + 12) % 12;
23677             }
23678         } else {
23679             // For magnitudes >1, move one month at a time...
23680             for (var i=0; i<mag; i++) {
23681                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23682                 new_date = this.moveMonth(new_date, dir);
23683             }
23684             // ...then reset the day, keeping it in the new month
23685             new_month = new_date.getUTCMonth();
23686             new_date.setUTCDate(day);
23687             test = function(){
23688                 return new_month != new_date.getUTCMonth();
23689             };
23690         }
23691         // Common date-resetting loop -- if date is beyond end of month, make it
23692         // end of month
23693         while (test()){
23694             new_date.setUTCDate(--day);
23695             new_date.setUTCMonth(new_month);
23696         }
23697         return new_date;
23698     },
23699
23700     moveYear: function(date, dir)
23701     {
23702         return this.moveMonth(date, dir*12);
23703     },
23704
23705     dateWithinRange: function(date)
23706     {
23707         return date >= this.startDate && date <= this.endDate;
23708     },
23709
23710     
23711     remove: function() 
23712     {
23713         this.picker().remove();
23714     },
23715     
23716     validateValue : function(value)
23717     {
23718         if(this.getVisibilityEl().hasClass('hidden')){
23719             return true;
23720         }
23721         
23722         if(value.length < 1)  {
23723             if(this.allowBlank){
23724                 return true;
23725             }
23726             return false;
23727         }
23728         
23729         if(value.length < this.minLength){
23730             return false;
23731         }
23732         if(value.length > this.maxLength){
23733             return false;
23734         }
23735         if(this.vtype){
23736             var vt = Roo.form.VTypes;
23737             if(!vt[this.vtype](value, this)){
23738                 return false;
23739             }
23740         }
23741         if(typeof this.validator == "function"){
23742             var msg = this.validator(value);
23743             if(msg !== true){
23744                 return false;
23745             }
23746         }
23747         
23748         if(this.regex && !this.regex.test(value)){
23749             return false;
23750         }
23751         
23752         if(typeof(this.parseDate(value)) == 'undefined'){
23753             return false;
23754         }
23755         
23756         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23757             return false;
23758         }      
23759         
23760         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23761             return false;
23762         } 
23763         
23764         
23765         return true;
23766     },
23767     
23768     reset : function()
23769     {
23770         this.date = this.viewDate = '';
23771         
23772         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23773     }
23774    
23775 });
23776
23777 Roo.apply(Roo.bootstrap.form.DateField,  {
23778     
23779     head : {
23780         tag: 'thead',
23781         cn: [
23782         {
23783             tag: 'tr',
23784             cn: [
23785             {
23786                 tag: 'th',
23787                 cls: 'prev',
23788                 html: '<i class="fa fa-arrow-left"/>'
23789             },
23790             {
23791                 tag: 'th',
23792                 cls: 'switch',
23793                 colspan: '5'
23794             },
23795             {
23796                 tag: 'th',
23797                 cls: 'next',
23798                 html: '<i class="fa fa-arrow-right"/>'
23799             }
23800
23801             ]
23802         }
23803         ]
23804     },
23805     
23806     content : {
23807         tag: 'tbody',
23808         cn: [
23809         {
23810             tag: 'tr',
23811             cn: [
23812             {
23813                 tag: 'td',
23814                 colspan: '7'
23815             }
23816             ]
23817         }
23818         ]
23819     },
23820     
23821     footer : {
23822         tag: 'tfoot',
23823         cn: [
23824         {
23825             tag: 'tr',
23826             cn: [
23827             {
23828                 tag: 'th',
23829                 colspan: '7',
23830                 cls: 'today'
23831             }
23832                     
23833             ]
23834         }
23835         ]
23836     },
23837     
23838     dates:{
23839         en: {
23840             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23841             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23842             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23843             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23844             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23845             today: "Today"
23846         }
23847     },
23848     
23849     modes: [
23850     {
23851         clsName: 'days',
23852         navFnc: 'Month',
23853         navStep: 1
23854     },
23855     {
23856         clsName: 'months',
23857         navFnc: 'FullYear',
23858         navStep: 1
23859     },
23860     {
23861         clsName: 'years',
23862         navFnc: 'FullYear',
23863         navStep: 10
23864     }]
23865 });
23866
23867 Roo.apply(Roo.bootstrap.form.DateField,  {
23868   
23869     template : {
23870         tag: 'div',
23871         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23872         cn: [
23873         {
23874             tag: 'div',
23875             cls: 'datepicker-days',
23876             cn: [
23877             {
23878                 tag: 'table',
23879                 cls: 'table-condensed',
23880                 cn:[
23881                 Roo.bootstrap.form.DateField.head,
23882                 {
23883                     tag: 'tbody'
23884                 },
23885                 Roo.bootstrap.form.DateField.footer
23886                 ]
23887             }
23888             ]
23889         },
23890         {
23891             tag: 'div',
23892             cls: 'datepicker-months',
23893             cn: [
23894             {
23895                 tag: 'table',
23896                 cls: 'table-condensed',
23897                 cn:[
23898                 Roo.bootstrap.form.DateField.head,
23899                 Roo.bootstrap.form.DateField.content,
23900                 Roo.bootstrap.form.DateField.footer
23901                 ]
23902             }
23903             ]
23904         },
23905         {
23906             tag: 'div',
23907             cls: 'datepicker-years',
23908             cn: [
23909             {
23910                 tag: 'table',
23911                 cls: 'table-condensed',
23912                 cn:[
23913                 Roo.bootstrap.form.DateField.head,
23914                 Roo.bootstrap.form.DateField.content,
23915                 Roo.bootstrap.form.DateField.footer
23916                 ]
23917             }
23918             ]
23919         }
23920         ]
23921     }
23922 });
23923
23924  
23925
23926  /*
23927  * - LGPL
23928  *
23929  * TimeField
23930  * 
23931  */
23932
23933 /**
23934  * @class Roo.bootstrap.form.TimeField
23935  * @extends Roo.bootstrap.form.Input
23936  * Bootstrap DateField class
23937  * 
23938  * 
23939  * @constructor
23940  * Create a new TimeField
23941  * @param {Object} config The config object
23942  */
23943
23944 Roo.bootstrap.form.TimeField = function(config){
23945     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23946     this.addEvents({
23947             /**
23948              * @event show
23949              * Fires when this field show.
23950              * @param {Roo.bootstrap.form.DateField} thisthis
23951              * @param {Mixed} date The date value
23952              */
23953             show : true,
23954             /**
23955              * @event show
23956              * Fires when this field hide.
23957              * @param {Roo.bootstrap.form.DateField} this
23958              * @param {Mixed} date The date value
23959              */
23960             hide : true,
23961             /**
23962              * @event select
23963              * Fires when select a date.
23964              * @param {Roo.bootstrap.form.DateField} this
23965              * @param {Mixed} date The date value
23966              */
23967             select : true
23968         });
23969 };
23970
23971 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23972     
23973     /**
23974      * @cfg {String} format
23975      * The default time format string which can be overriden for localization support.  The format must be
23976      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23977      */
23978     format : "H:i",
23979
23980     getAutoCreate : function()
23981     {
23982         this.after = '<i class="fa far fa-clock"></i>';
23983         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23984         
23985          
23986     },
23987     onRender: function(ct, position)
23988     {
23989         
23990         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23991                 
23992         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23993         
23994         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23995         
23996         this.pop = this.picker().select('>.datepicker-time',true).first();
23997         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23998         
23999         this.picker().on('mousedown', this.onMousedown, this);
24000         this.picker().on('click', this.onClick, this);
24001         
24002         this.picker().addClass('datepicker-dropdown');
24003     
24004         this.fillTime();
24005         this.update();
24006             
24007         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24008         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24009         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24010         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24011         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24012         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24013
24014     },
24015     
24016     fireKey: function(e){
24017         if (!this.picker().isVisible()){
24018             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24019                 this.show();
24020             }
24021             return;
24022         }
24023
24024         e.preventDefault();
24025         
24026         switch(e.keyCode){
24027             case 27: // escape
24028                 this.hide();
24029                 break;
24030             case 37: // left
24031             case 39: // right
24032                 this.onTogglePeriod();
24033                 break;
24034             case 38: // up
24035                 this.onIncrementMinutes();
24036                 break;
24037             case 40: // down
24038                 this.onDecrementMinutes();
24039                 break;
24040             case 13: // enter
24041             case 9: // tab
24042                 this.setTime();
24043                 break;
24044         }
24045     },
24046     
24047     onClick: function(e) {
24048         e.stopPropagation();
24049         e.preventDefault();
24050     },
24051     
24052     picker : function()
24053     {
24054         return this.pickerEl;
24055     },
24056     
24057     fillTime: function()
24058     {    
24059         var time = this.pop.select('tbody', true).first();
24060         
24061         time.dom.innerHTML = '';
24062         
24063         time.createChild({
24064             tag: 'tr',
24065             cn: [
24066                 {
24067                     tag: 'td',
24068                     cn: [
24069                         {
24070                             tag: 'a',
24071                             href: '#',
24072                             cls: 'btn',
24073                             cn: [
24074                                 {
24075                                     tag: 'i',
24076                                     cls: 'hours-up fa fas fa-chevron-up'
24077                                 }
24078                             ]
24079                         } 
24080                     ]
24081                 },
24082                 {
24083                     tag: 'td',
24084                     cls: 'separator'
24085                 },
24086                 {
24087                     tag: 'td',
24088                     cn: [
24089                         {
24090                             tag: 'a',
24091                             href: '#',
24092                             cls: 'btn',
24093                             cn: [
24094                                 {
24095                                     tag: 'i',
24096                                     cls: 'minutes-up fa fas fa-chevron-up'
24097                                 }
24098                             ]
24099                         }
24100                     ]
24101                 },
24102                 {
24103                     tag: 'td',
24104                     cls: 'separator'
24105                 }
24106             ]
24107         });
24108         
24109         time.createChild({
24110             tag: 'tr',
24111             cn: [
24112                 {
24113                     tag: 'td',
24114                     cn: [
24115                         {
24116                             tag: 'span',
24117                             cls: 'timepicker-hour',
24118                             html: '00'
24119                         }  
24120                     ]
24121                 },
24122                 {
24123                     tag: 'td',
24124                     cls: 'separator',
24125                     html: ':'
24126                 },
24127                 {
24128                     tag: 'td',
24129                     cn: [
24130                         {
24131                             tag: 'span',
24132                             cls: 'timepicker-minute',
24133                             html: '00'
24134                         }  
24135                     ]
24136                 },
24137                 {
24138                     tag: 'td',
24139                     cls: 'separator'
24140                 },
24141                 {
24142                     tag: 'td',
24143                     cn: [
24144                         {
24145                             tag: 'button',
24146                             type: 'button',
24147                             cls: 'btn btn-primary period',
24148                             html: 'AM'
24149                             
24150                         }
24151                     ]
24152                 }
24153             ]
24154         });
24155         
24156         time.createChild({
24157             tag: 'tr',
24158             cn: [
24159                 {
24160                     tag: 'td',
24161                     cn: [
24162                         {
24163                             tag: 'a',
24164                             href: '#',
24165                             cls: 'btn',
24166                             cn: [
24167                                 {
24168                                     tag: 'span',
24169                                     cls: 'hours-down fa fas fa-chevron-down'
24170                                 }
24171                             ]
24172                         }
24173                     ]
24174                 },
24175                 {
24176                     tag: 'td',
24177                     cls: 'separator'
24178                 },
24179                 {
24180                     tag: 'td',
24181                     cn: [
24182                         {
24183                             tag: 'a',
24184                             href: '#',
24185                             cls: 'btn',
24186                             cn: [
24187                                 {
24188                                     tag: 'span',
24189                                     cls: 'minutes-down fa fas fa-chevron-down'
24190                                 }
24191                             ]
24192                         }
24193                     ]
24194                 },
24195                 {
24196                     tag: 'td',
24197                     cls: 'separator'
24198                 }
24199             ]
24200         });
24201         
24202     },
24203     
24204     update: function()
24205     {
24206         
24207         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24208         
24209         this.fill();
24210     },
24211     
24212     fill: function() 
24213     {
24214         var hours = this.time.getHours();
24215         var minutes = this.time.getMinutes();
24216         var period = 'AM';
24217         
24218         if(hours > 11){
24219             period = 'PM';
24220         }
24221         
24222         if(hours == 0){
24223             hours = 12;
24224         }
24225         
24226         
24227         if(hours > 12){
24228             hours = hours - 12;
24229         }
24230         
24231         if(hours < 10){
24232             hours = '0' + hours;
24233         }
24234         
24235         if(minutes < 10){
24236             minutes = '0' + minutes;
24237         }
24238         
24239         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24240         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24241         this.pop.select('button', true).first().dom.innerHTML = period;
24242         
24243     },
24244     
24245     place: function()
24246     {   
24247         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24248         
24249         var cls = ['bottom'];
24250         
24251         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24252             cls.pop();
24253             cls.push('top');
24254         }
24255         
24256         cls.push('right');
24257         
24258         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24259             cls.pop();
24260             cls.push('left');
24261         }
24262         //this.picker().setXY(20000,20000);
24263         this.picker().addClass(cls.join('-'));
24264         
24265         var _this = this;
24266         
24267         Roo.each(cls, function(c){
24268             if(c == 'bottom'){
24269                 (function() {
24270                  //  
24271                 }).defer(200);
24272                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24273                 //_this.picker().setTop(_this.inputEl().getHeight());
24274                 return;
24275             }
24276             if(c == 'top'){
24277                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24278                 
24279                 //_this.picker().setTop(0 - _this.picker().getHeight());
24280                 return;
24281             }
24282             /*
24283             if(c == 'left'){
24284                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24285                 return;
24286             }
24287             if(c == 'right'){
24288                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24289                 return;
24290             }
24291             */
24292         });
24293         
24294     },
24295   
24296     onFocus : function()
24297     {
24298         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24299         this.show();
24300     },
24301     
24302     onBlur : function()
24303     {
24304         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24305         this.hide();
24306     },
24307     
24308     show : function()
24309     {
24310         this.picker().show();
24311         this.pop.show();
24312         this.update();
24313         this.place();
24314         
24315         this.fireEvent('show', this, this.date);
24316     },
24317     
24318     hide : function()
24319     {
24320         this.picker().hide();
24321         this.pop.hide();
24322         
24323         this.fireEvent('hide', this, this.date);
24324     },
24325     
24326     setTime : function()
24327     {
24328         this.hide();
24329         this.setValue(this.time.format(this.format));
24330         
24331         this.fireEvent('select', this, this.date);
24332         
24333         
24334     },
24335     
24336     onMousedown: function(e){
24337         e.stopPropagation();
24338         e.preventDefault();
24339     },
24340     
24341     onIncrementHours: function()
24342     {
24343         Roo.log('onIncrementHours');
24344         this.time = this.time.add(Date.HOUR, 1);
24345         this.update();
24346         
24347     },
24348     
24349     onDecrementHours: function()
24350     {
24351         Roo.log('onDecrementHours');
24352         this.time = this.time.add(Date.HOUR, -1);
24353         this.update();
24354     },
24355     
24356     onIncrementMinutes: function()
24357     {
24358         Roo.log('onIncrementMinutes');
24359         this.time = this.time.add(Date.MINUTE, 1);
24360         this.update();
24361     },
24362     
24363     onDecrementMinutes: function()
24364     {
24365         Roo.log('onDecrementMinutes');
24366         this.time = this.time.add(Date.MINUTE, -1);
24367         this.update();
24368     },
24369     
24370     onTogglePeriod: function()
24371     {
24372         Roo.log('onTogglePeriod');
24373         this.time = this.time.add(Date.HOUR, 12);
24374         this.update();
24375     }
24376     
24377    
24378 });
24379  
24380
24381 Roo.apply(Roo.bootstrap.form.TimeField,  {
24382   
24383     template : {
24384         tag: 'div',
24385         cls: 'datepicker dropdown-menu',
24386         cn: [
24387             {
24388                 tag: 'div',
24389                 cls: 'datepicker-time',
24390                 cn: [
24391                 {
24392                     tag: 'table',
24393                     cls: 'table-condensed',
24394                     cn:[
24395                         {
24396                             tag: 'tbody',
24397                             cn: [
24398                                 {
24399                                     tag: 'tr',
24400                                     cn: [
24401                                     {
24402                                         tag: 'td',
24403                                         colspan: '7'
24404                                     }
24405                                     ]
24406                                 }
24407                             ]
24408                         },
24409                         {
24410                             tag: 'tfoot',
24411                             cn: [
24412                                 {
24413                                     tag: 'tr',
24414                                     cn: [
24415                                     {
24416                                         tag: 'th',
24417                                         colspan: '7',
24418                                         cls: '',
24419                                         cn: [
24420                                             {
24421                                                 tag: 'button',
24422                                                 cls: 'btn btn-info ok',
24423                                                 html: 'OK'
24424                                             }
24425                                         ]
24426                                     }
24427                     
24428                                     ]
24429                                 }
24430                             ]
24431                         }
24432                     ]
24433                 }
24434                 ]
24435             }
24436         ]
24437     }
24438 });
24439
24440  
24441
24442  /*
24443  * - LGPL
24444  *
24445  * MonthField
24446  * 
24447  */
24448
24449 /**
24450  * @class Roo.bootstrap.form.MonthField
24451  * @extends Roo.bootstrap.form.Input
24452  * Bootstrap MonthField class
24453  * 
24454  * @cfg {String} language default en
24455  * 
24456  * @constructor
24457  * Create a new MonthField
24458  * @param {Object} config The config object
24459  */
24460
24461 Roo.bootstrap.form.MonthField = function(config){
24462     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24463     
24464     this.addEvents({
24465         /**
24466          * @event show
24467          * Fires when this field show.
24468          * @param {Roo.bootstrap.form.MonthField} this
24469          * @param {Mixed} date The date value
24470          */
24471         show : true,
24472         /**
24473          * @event show
24474          * Fires when this field hide.
24475          * @param {Roo.bootstrap.form.MonthField} this
24476          * @param {Mixed} date The date value
24477          */
24478         hide : true,
24479         /**
24480          * @event select
24481          * Fires when select a date.
24482          * @param {Roo.bootstrap.form.MonthField} this
24483          * @param {String} oldvalue The old value
24484          * @param {String} newvalue The new value
24485          */
24486         select : true
24487     });
24488 };
24489
24490 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24491     
24492     onRender: function(ct, position)
24493     {
24494         
24495         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24496         
24497         this.language = this.language || 'en';
24498         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24499         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24500         
24501         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24502         this.isInline = false;
24503         this.isInput = true;
24504         this.component = this.el.select('.add-on', true).first() || false;
24505         this.component = (this.component && this.component.length === 0) ? false : this.component;
24506         this.hasInput = this.component && this.inputEL().length;
24507         
24508         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24509         
24510         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24511         
24512         this.picker().on('mousedown', this.onMousedown, this);
24513         this.picker().on('click', this.onClick, this);
24514         
24515         this.picker().addClass('datepicker-dropdown');
24516         
24517         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24518             v.setStyle('width', '189px');
24519         });
24520         
24521         this.fillMonths();
24522         
24523         this.update();
24524         
24525         if(this.isInline) {
24526             this.show();
24527         }
24528         
24529     },
24530     
24531     setValue: function(v, suppressEvent)
24532     {   
24533         var o = this.getValue();
24534         
24535         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24536         
24537         this.update();
24538
24539         if(suppressEvent !== true){
24540             this.fireEvent('select', this, o, v);
24541         }
24542         
24543     },
24544     
24545     getValue: function()
24546     {
24547         return this.value;
24548     },
24549     
24550     onClick: function(e) 
24551     {
24552         e.stopPropagation();
24553         e.preventDefault();
24554         
24555         var target = e.getTarget();
24556         
24557         if(target.nodeName.toLowerCase() === 'i'){
24558             target = Roo.get(target).dom.parentNode;
24559         }
24560         
24561         var nodeName = target.nodeName;
24562         var className = target.className;
24563         var html = target.innerHTML;
24564         
24565         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24566             return;
24567         }
24568         
24569         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24570         
24571         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24572         
24573         this.hide();
24574                         
24575     },
24576     
24577     picker : function()
24578     {
24579         return this.pickerEl;
24580     },
24581     
24582     fillMonths: function()
24583     {    
24584         var i = 0;
24585         var months = this.picker().select('>.datepicker-months td', true).first();
24586         
24587         months.dom.innerHTML = '';
24588         
24589         while (i < 12) {
24590             var month = {
24591                 tag: 'span',
24592                 cls: 'month',
24593                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24594             };
24595             
24596             months.createChild(month);
24597         }
24598         
24599     },
24600     
24601     update: function()
24602     {
24603         var _this = this;
24604         
24605         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24606             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24607         }
24608         
24609         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24610             e.removeClass('active');
24611             
24612             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24613                 e.addClass('active');
24614             }
24615         })
24616     },
24617     
24618     place: function()
24619     {
24620         if(this.isInline) {
24621             return;
24622         }
24623         
24624         this.picker().removeClass(['bottom', 'top']);
24625         
24626         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24627             /*
24628              * place to the top of element!
24629              *
24630              */
24631             
24632             this.picker().addClass('top');
24633             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24634             
24635             return;
24636         }
24637         
24638         this.picker().addClass('bottom');
24639         
24640         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24641     },
24642     
24643     onFocus : function()
24644     {
24645         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24646         this.show();
24647     },
24648     
24649     onBlur : function()
24650     {
24651         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24652         
24653         var d = this.inputEl().getValue();
24654         
24655         this.setValue(d);
24656                 
24657         this.hide();
24658     },
24659     
24660     show : function()
24661     {
24662         this.picker().show();
24663         this.picker().select('>.datepicker-months', true).first().show();
24664         this.update();
24665         this.place();
24666         
24667         this.fireEvent('show', this, this.date);
24668     },
24669     
24670     hide : function()
24671     {
24672         if(this.isInline) {
24673             return;
24674         }
24675         this.picker().hide();
24676         this.fireEvent('hide', this, this.date);
24677         
24678     },
24679     
24680     onMousedown: function(e)
24681     {
24682         e.stopPropagation();
24683         e.preventDefault();
24684     },
24685     
24686     keyup: function(e)
24687     {
24688         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24689         this.update();
24690     },
24691
24692     fireKey: function(e)
24693     {
24694         if (!this.picker().isVisible()){
24695             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24696                 this.show();
24697             }
24698             return;
24699         }
24700         
24701         var dir;
24702         
24703         switch(e.keyCode){
24704             case 27: // escape
24705                 this.hide();
24706                 e.preventDefault();
24707                 break;
24708             case 37: // left
24709             case 39: // right
24710                 dir = e.keyCode == 37 ? -1 : 1;
24711                 
24712                 this.vIndex = this.vIndex + dir;
24713                 
24714                 if(this.vIndex < 0){
24715                     this.vIndex = 0;
24716                 }
24717                 
24718                 if(this.vIndex > 11){
24719                     this.vIndex = 11;
24720                 }
24721                 
24722                 if(isNaN(this.vIndex)){
24723                     this.vIndex = 0;
24724                 }
24725                 
24726                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24727                 
24728                 break;
24729             case 38: // up
24730             case 40: // down
24731                 
24732                 dir = e.keyCode == 38 ? -1 : 1;
24733                 
24734                 this.vIndex = this.vIndex + dir * 4;
24735                 
24736                 if(this.vIndex < 0){
24737                     this.vIndex = 0;
24738                 }
24739                 
24740                 if(this.vIndex > 11){
24741                     this.vIndex = 11;
24742                 }
24743                 
24744                 if(isNaN(this.vIndex)){
24745                     this.vIndex = 0;
24746                 }
24747                 
24748                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24749                 break;
24750                 
24751             case 13: // enter
24752                 
24753                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24754                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24755                 }
24756                 
24757                 this.hide();
24758                 e.preventDefault();
24759                 break;
24760             case 9: // tab
24761                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24762                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24763                 }
24764                 this.hide();
24765                 break;
24766             case 16: // shift
24767             case 17: // ctrl
24768             case 18: // alt
24769                 break;
24770             default :
24771                 this.hide();
24772                 
24773         }
24774     },
24775     
24776     remove: function() 
24777     {
24778         this.picker().remove();
24779     }
24780    
24781 });
24782
24783 Roo.apply(Roo.bootstrap.form.MonthField,  {
24784     
24785     content : {
24786         tag: 'tbody',
24787         cn: [
24788         {
24789             tag: 'tr',
24790             cn: [
24791             {
24792                 tag: 'td',
24793                 colspan: '7'
24794             }
24795             ]
24796         }
24797         ]
24798     },
24799     
24800     dates:{
24801         en: {
24802             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24803             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24804         }
24805     }
24806 });
24807
24808 Roo.apply(Roo.bootstrap.form.MonthField,  {
24809   
24810     template : {
24811         tag: 'div',
24812         cls: 'datepicker dropdown-menu roo-dynamic',
24813         cn: [
24814             {
24815                 tag: 'div',
24816                 cls: 'datepicker-months',
24817                 cn: [
24818                 {
24819                     tag: 'table',
24820                     cls: 'table-condensed',
24821                     cn:[
24822                         Roo.bootstrap.form.DateField.content
24823                     ]
24824                 }
24825                 ]
24826             }
24827         ]
24828     }
24829 });
24830
24831  
24832
24833  
24834  /*
24835  * - LGPL
24836  *
24837  * CheckBox
24838  * 
24839  */
24840
24841 /**
24842  * @class Roo.bootstrap.form.CheckBox
24843  * @extends Roo.bootstrap.form.Input
24844  * Bootstrap CheckBox class
24845  * 
24846  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24847  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24848  * @cfg {String} boxLabel The text that appears beside the checkbox
24849  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24850  * @cfg {Boolean} checked initnal the element
24851  * @cfg {Boolean} inline inline the element (default false)
24852  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24853  * @cfg {String} tooltip label tooltip
24854  * 
24855  * @constructor
24856  * Create a new CheckBox
24857  * @param {Object} config The config object
24858  */
24859
24860 Roo.bootstrap.form.CheckBox = function(config){
24861     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24862    
24863     this.addEvents({
24864         /**
24865         * @event check
24866         * Fires when the element is checked or unchecked.
24867         * @param {Roo.bootstrap.form.CheckBox} this This input
24868         * @param {Boolean} checked The new checked value
24869         */
24870        check : true,
24871        /**
24872         * @event click
24873         * Fires when the element is click.
24874         * @param {Roo.bootstrap.form.CheckBox} this This input
24875         */
24876        click : true
24877     });
24878     
24879 };
24880
24881 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24882   
24883     inputType: 'checkbox',
24884     inputValue: 1,
24885     valueOff: 0,
24886     boxLabel: false,
24887     checked: false,
24888     weight : false,
24889     inline: false,
24890     tooltip : '',
24891     
24892     // checkbox success does not make any sense really.. 
24893     invalidClass : "",
24894     validClass : "",
24895     
24896     
24897     getAutoCreate : function()
24898     {
24899         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24900         
24901         var id = Roo.id();
24902         
24903         var cfg = {};
24904         
24905         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24906         
24907         if(this.inline){
24908             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24909         }
24910         
24911         var input =  {
24912             tag: 'input',
24913             id : id,
24914             type : this.inputType,
24915             value : this.inputValue,
24916             cls : 'roo-' + this.inputType, //'form-box',
24917             placeholder : this.placeholder || ''
24918             
24919         };
24920         
24921         if(this.inputType != 'radio'){
24922             var hidden =  {
24923                 tag: 'input',
24924                 type : 'hidden',
24925                 cls : 'roo-hidden-value',
24926                 value : this.checked ? this.inputValue : this.valueOff
24927             };
24928         }
24929         
24930             
24931         if (this.weight) { // Validity check?
24932             cfg.cls += " " + this.inputType + "-" + this.weight;
24933         }
24934         
24935         if (this.disabled) {
24936             input.disabled=true;
24937         }
24938         
24939         if(this.checked){
24940             input.checked = this.checked;
24941         }
24942         
24943         if (this.name) {
24944             
24945             input.name = this.name;
24946             
24947             if(this.inputType != 'radio'){
24948                 hidden.name = this.name;
24949                 input.name = '_hidden_' + this.name;
24950             }
24951         }
24952         
24953         if (this.size) {
24954             input.cls += ' input-' + this.size;
24955         }
24956         
24957         var settings=this;
24958         
24959         ['xs','sm','md','lg'].map(function(size){
24960             if (settings[size]) {
24961                 cfg.cls += ' col-' + size + '-' + settings[size];
24962             }
24963         });
24964         
24965         var inputblock = input;
24966          
24967         if (this.before || this.after) {
24968             
24969             inputblock = {
24970                 cls : 'input-group',
24971                 cn :  [] 
24972             };
24973             
24974             if (this.before) {
24975                 inputblock.cn.push({
24976                     tag :'span',
24977                     cls : 'input-group-addon',
24978                     html : this.before
24979                 });
24980             }
24981             
24982             inputblock.cn.push(input);
24983             
24984             if(this.inputType != 'radio'){
24985                 inputblock.cn.push(hidden);
24986             }
24987             
24988             if (this.after) {
24989                 inputblock.cn.push({
24990                     tag :'span',
24991                     cls : 'input-group-addon',
24992                     html : this.after
24993                 });
24994             }
24995             
24996         }
24997         var boxLabelCfg = false;
24998         
24999         if(this.boxLabel){
25000            
25001             boxLabelCfg = {
25002                 tag: 'label',
25003                 //'for': id, // box label is handled by onclick - so no for...
25004                 cls: 'box-label',
25005                 html: this.boxLabel
25006             };
25007             if(this.tooltip){
25008                 boxLabelCfg.tooltip = this.tooltip;
25009             }
25010              
25011         }
25012         
25013         
25014         if (align ==='left' && this.fieldLabel.length) {
25015 //                Roo.log("left and has label");
25016             cfg.cn = [
25017                 {
25018                     tag: 'label',
25019                     'for' :  id,
25020                     cls : 'control-label',
25021                     html : this.fieldLabel
25022                 },
25023                 {
25024                     cls : "", 
25025                     cn: [
25026                         inputblock
25027                     ]
25028                 }
25029             ];
25030             
25031             if (boxLabelCfg) {
25032                 cfg.cn[1].cn.push(boxLabelCfg);
25033             }
25034             
25035             if(this.labelWidth > 12){
25036                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25037             }
25038             
25039             if(this.labelWidth < 13 && this.labelmd == 0){
25040                 this.labelmd = this.labelWidth;
25041             }
25042             
25043             if(this.labellg > 0){
25044                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25045                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25046             }
25047             
25048             if(this.labelmd > 0){
25049                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25050                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25051             }
25052             
25053             if(this.labelsm > 0){
25054                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25055                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25056             }
25057             
25058             if(this.labelxs > 0){
25059                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25060                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25061             }
25062             
25063         } else if ( this.fieldLabel.length) {
25064 //                Roo.log(" label");
25065                 cfg.cn = [
25066                    
25067                     {
25068                         tag: this.boxLabel ? 'span' : 'label',
25069                         'for': id,
25070                         cls: 'control-label box-input-label',
25071                         //cls : 'input-group-addon',
25072                         html : this.fieldLabel
25073                     },
25074                     
25075                     inputblock
25076                     
25077                 ];
25078                 if (boxLabelCfg) {
25079                     cfg.cn.push(boxLabelCfg);
25080                 }
25081
25082         } else {
25083             
25084 //                Roo.log(" no label && no align");
25085                 cfg.cn = [  inputblock ] ;
25086                 if (boxLabelCfg) {
25087                     cfg.cn.push(boxLabelCfg);
25088                 }
25089
25090                 
25091         }
25092         
25093        
25094         
25095         if(this.inputType != 'radio'){
25096             cfg.cn.push(hidden);
25097         }
25098         
25099         return cfg;
25100         
25101     },
25102     
25103     /**
25104      * return the real input element.
25105      */
25106     inputEl: function ()
25107     {
25108         return this.el.select('input.roo-' + this.inputType,true).first();
25109     },
25110     hiddenEl: function ()
25111     {
25112         return this.el.select('input.roo-hidden-value',true).first();
25113     },
25114     
25115     labelEl: function()
25116     {
25117         return this.el.select('label.control-label',true).first();
25118     },
25119     /* depricated... */
25120     
25121     label: function()
25122     {
25123         return this.labelEl();
25124     },
25125     
25126     boxLabelEl: function()
25127     {
25128         return this.el.select('label.box-label',true).first();
25129     },
25130     
25131     initEvents : function()
25132     {
25133 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25134         
25135         this.inputEl().on('click', this.onClick,  this);
25136         
25137         if (this.boxLabel) { 
25138             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25139         }
25140         
25141         this.startValue = this.getValue();
25142         
25143         if(this.groupId){
25144             Roo.bootstrap.form.CheckBox.register(this);
25145         }
25146     },
25147     
25148     onClick : function(e)
25149     {   
25150         if(this.fireEvent('click', this, e) !== false){
25151             this.setChecked(!this.checked);
25152         }
25153         
25154     },
25155     
25156     setChecked : function(state,suppressEvent)
25157     {
25158         this.startValue = this.getValue();
25159
25160         if(this.inputType == 'radio'){
25161             
25162             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25163                 e.dom.checked = false;
25164             });
25165             
25166             this.inputEl().dom.checked = true;
25167             
25168             this.inputEl().dom.value = this.inputValue;
25169             
25170             if(suppressEvent !== true){
25171                 this.fireEvent('check', this, true);
25172             }
25173             
25174             this.validate();
25175             
25176             return;
25177         }
25178         
25179         this.checked = state;
25180         
25181         this.inputEl().dom.checked = state;
25182         
25183         
25184         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25185         
25186         if(suppressEvent !== true){
25187             this.fireEvent('check', this, state);
25188         }
25189         
25190         this.validate();
25191     },
25192     
25193     getValue : function()
25194     {
25195         if(this.inputType == 'radio'){
25196             return this.getGroupValue();
25197         }
25198         
25199         return this.hiddenEl().dom.value;
25200         
25201     },
25202     
25203     getGroupValue : function()
25204     {
25205         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25206             return '';
25207         }
25208         
25209         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25210     },
25211     
25212     setValue : function(v,suppressEvent)
25213     {
25214         if(this.inputType == 'radio'){
25215             this.setGroupValue(v, suppressEvent);
25216             return;
25217         }
25218         
25219         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25220         
25221         this.validate();
25222     },
25223     
25224     setGroupValue : function(v, suppressEvent)
25225     {
25226         this.startValue = this.getValue();
25227         
25228         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25229             e.dom.checked = false;
25230             
25231             if(e.dom.value == v){
25232                 e.dom.checked = true;
25233             }
25234         });
25235         
25236         if(suppressEvent !== true){
25237             this.fireEvent('check', this, true);
25238         }
25239
25240         this.validate();
25241         
25242         return;
25243     },
25244     
25245     validate : function()
25246     {
25247         if(this.getVisibilityEl().hasClass('hidden')){
25248             return true;
25249         }
25250         
25251         if(
25252                 this.disabled || 
25253                 (this.inputType == 'radio' && this.validateRadio()) ||
25254                 (this.inputType == 'checkbox' && this.validateCheckbox())
25255         ){
25256             this.markValid();
25257             return true;
25258         }
25259         
25260         this.markInvalid();
25261         return false;
25262     },
25263     
25264     validateRadio : function()
25265     {
25266         if(this.getVisibilityEl().hasClass('hidden')){
25267             return true;
25268         }
25269         
25270         if(this.allowBlank){
25271             return true;
25272         }
25273         
25274         var valid = false;
25275         
25276         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25277             if(!e.dom.checked){
25278                 return;
25279             }
25280             
25281             valid = true;
25282             
25283             return false;
25284         });
25285         
25286         return valid;
25287     },
25288     
25289     validateCheckbox : function()
25290     {
25291         if(!this.groupId){
25292             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25293             //return (this.getValue() == this.inputValue) ? true : false;
25294         }
25295         
25296         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25297         
25298         if(!group){
25299             return false;
25300         }
25301         
25302         var r = false;
25303         
25304         for(var i in group){
25305             if(group[i].el.isVisible(true)){
25306                 r = false;
25307                 break;
25308             }
25309             
25310             r = true;
25311         }
25312         
25313         for(var i in group){
25314             if(r){
25315                 break;
25316             }
25317             
25318             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25319         }
25320         
25321         return r;
25322     },
25323     
25324     /**
25325      * Mark this field as valid
25326      */
25327     markValid : function()
25328     {
25329         var _this = this;
25330         
25331         this.fireEvent('valid', this);
25332         
25333         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25334         
25335         if(this.groupId){
25336             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25337         }
25338         
25339         if(label){
25340             label.markValid();
25341         }
25342
25343         if(this.inputType == 'radio'){
25344             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25345                 var fg = e.findParent('.form-group', false, true);
25346                 if (Roo.bootstrap.version == 3) {
25347                     fg.removeClass([_this.invalidClass, _this.validClass]);
25348                     fg.addClass(_this.validClass);
25349                 } else {
25350                     fg.removeClass(['is-valid', 'is-invalid']);
25351                     fg.addClass('is-valid');
25352                 }
25353             });
25354             
25355             return;
25356         }
25357
25358         if(!this.groupId){
25359             var fg = this.el.findParent('.form-group', false, true);
25360             if (Roo.bootstrap.version == 3) {
25361                 fg.removeClass([this.invalidClass, this.validClass]);
25362                 fg.addClass(this.validClass);
25363             } else {
25364                 fg.removeClass(['is-valid', 'is-invalid']);
25365                 fg.addClass('is-valid');
25366             }
25367             return;
25368         }
25369         
25370         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25371         
25372         if(!group){
25373             return;
25374         }
25375         
25376         for(var i in group){
25377             var fg = group[i].el.findParent('.form-group', false, true);
25378             if (Roo.bootstrap.version == 3) {
25379                 fg.removeClass([this.invalidClass, this.validClass]);
25380                 fg.addClass(this.validClass);
25381             } else {
25382                 fg.removeClass(['is-valid', 'is-invalid']);
25383                 fg.addClass('is-valid');
25384             }
25385         }
25386     },
25387     
25388      /**
25389      * Mark this field as invalid
25390      * @param {String} msg The validation message
25391      */
25392     markInvalid : function(msg)
25393     {
25394         if(this.allowBlank){
25395             return;
25396         }
25397         
25398         var _this = this;
25399         
25400         this.fireEvent('invalid', this, msg);
25401         
25402         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25403         
25404         if(this.groupId){
25405             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25406         }
25407         
25408         if(label){
25409             label.markInvalid();
25410         }
25411             
25412         if(this.inputType == 'radio'){
25413             
25414             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25415                 var fg = e.findParent('.form-group', false, true);
25416                 if (Roo.bootstrap.version == 3) {
25417                     fg.removeClass([_this.invalidClass, _this.validClass]);
25418                     fg.addClass(_this.invalidClass);
25419                 } else {
25420                     fg.removeClass(['is-invalid', 'is-valid']);
25421                     fg.addClass('is-invalid');
25422                 }
25423             });
25424             
25425             return;
25426         }
25427         
25428         if(!this.groupId){
25429             var fg = this.el.findParent('.form-group', false, true);
25430             if (Roo.bootstrap.version == 3) {
25431                 fg.removeClass([_this.invalidClass, _this.validClass]);
25432                 fg.addClass(_this.invalidClass);
25433             } else {
25434                 fg.removeClass(['is-invalid', 'is-valid']);
25435                 fg.addClass('is-invalid');
25436             }
25437             return;
25438         }
25439         
25440         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25441         
25442         if(!group){
25443             return;
25444         }
25445         
25446         for(var i in group){
25447             var fg = group[i].el.findParent('.form-group', false, true);
25448             if (Roo.bootstrap.version == 3) {
25449                 fg.removeClass([_this.invalidClass, _this.validClass]);
25450                 fg.addClass(_this.invalidClass);
25451             } else {
25452                 fg.removeClass(['is-invalid', 'is-valid']);
25453                 fg.addClass('is-invalid');
25454             }
25455         }
25456         
25457     },
25458     
25459     clearInvalid : function()
25460     {
25461         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25462         
25463         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25464         
25465         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25466         
25467         if (label && label.iconEl) {
25468             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25469             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25470         }
25471     },
25472     
25473     disable : function()
25474     {
25475         if(this.inputType != 'radio'){
25476             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25477             return;
25478         }
25479         
25480         var _this = this;
25481         
25482         if(this.rendered){
25483             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25484                 _this.getActionEl().addClass(this.disabledClass);
25485                 e.dom.disabled = true;
25486             });
25487         }
25488         
25489         this.disabled = true;
25490         this.fireEvent("disable", this);
25491         return this;
25492     },
25493
25494     enable : function()
25495     {
25496         if(this.inputType != 'radio'){
25497             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25498             return;
25499         }
25500         
25501         var _this = this;
25502         
25503         if(this.rendered){
25504             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25505                 _this.getActionEl().removeClass(this.disabledClass);
25506                 e.dom.disabled = false;
25507             });
25508         }
25509         
25510         this.disabled = false;
25511         this.fireEvent("enable", this);
25512         return this;
25513     },
25514     
25515     setBoxLabel : function(v)
25516     {
25517         this.boxLabel = v;
25518         
25519         if(this.rendered){
25520             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25521         }
25522     }
25523
25524 });
25525
25526 Roo.apply(Roo.bootstrap.form.CheckBox, {
25527     
25528     groups: {},
25529     
25530      /**
25531     * register a CheckBox Group
25532     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25533     */
25534     register : function(checkbox)
25535     {
25536         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25537             this.groups[checkbox.groupId] = {};
25538         }
25539         
25540         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25541             return;
25542         }
25543         
25544         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25545         
25546     },
25547     /**
25548     * fetch a CheckBox Group based on the group ID
25549     * @param {string} the group ID
25550     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25551     */
25552     get: function(groupId) {
25553         if (typeof(this.groups[groupId]) == 'undefined') {
25554             return false;
25555         }
25556         
25557         return this.groups[groupId] ;
25558     }
25559     
25560     
25561 });
25562 /*
25563  * - LGPL
25564  *
25565  * RadioItem
25566  * 
25567  */
25568
25569 /**
25570  * @class Roo.bootstrap.form.Radio
25571  * @extends Roo.bootstrap.Component
25572  * Bootstrap Radio class
25573  * @cfg {String} boxLabel - the label associated
25574  * @cfg {String} value - the value of radio
25575  * 
25576  * @constructor
25577  * Create a new Radio
25578  * @param {Object} config The config object
25579  */
25580 Roo.bootstrap.form.Radio = function(config){
25581     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25582     
25583 };
25584
25585 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25586     
25587     boxLabel : '',
25588     
25589     value : '',
25590     
25591     getAutoCreate : function()
25592     {
25593         var cfg = {
25594             tag : 'div',
25595             cls : 'form-group radio',
25596             cn : [
25597                 {
25598                     tag : 'label',
25599                     cls : 'box-label',
25600                     html : this.boxLabel
25601                 }
25602             ]
25603         };
25604         
25605         return cfg;
25606     },
25607     
25608     initEvents : function() 
25609     {
25610         this.parent().register(this);
25611         
25612         this.el.on('click', this.onClick, this);
25613         
25614     },
25615     
25616     onClick : function(e)
25617     {
25618         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25619             this.setChecked(true);
25620         }
25621     },
25622     
25623     setChecked : function(state, suppressEvent)
25624     {
25625         this.parent().setValue(this.value, suppressEvent);
25626         
25627     },
25628     
25629     setBoxLabel : function(v)
25630     {
25631         this.boxLabel = v;
25632         
25633         if(this.rendered){
25634             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25635         }
25636     }
25637     
25638 });
25639  
25640
25641  /*
25642  * - LGPL
25643  *
25644  * Input
25645  * 
25646  */
25647
25648 /**
25649  * @class Roo.bootstrap.form.SecurePass
25650  * @extends Roo.bootstrap.form.Input
25651  * Bootstrap SecurePass class
25652  *
25653  * 
25654  * @constructor
25655  * Create a new SecurePass
25656  * @param {Object} config The config object
25657  */
25658  
25659 Roo.bootstrap.form.SecurePass = function (config) {
25660     // these go here, so the translation tool can replace them..
25661     this.errors = {
25662         PwdEmpty: "Please type a password, and then retype it to confirm.",
25663         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25664         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25665         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25666         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25667         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25668         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25669         TooWeak: "Your password is Too Weak."
25670     },
25671     this.meterLabel = "Password strength:";
25672     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25673     this.meterClass = [
25674         "roo-password-meter-tooweak", 
25675         "roo-password-meter-weak", 
25676         "roo-password-meter-medium", 
25677         "roo-password-meter-strong", 
25678         "roo-password-meter-grey"
25679     ];
25680     
25681     this.errors = {};
25682     
25683     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25684 }
25685
25686 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25687     /**
25688      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25689      * {
25690      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25691      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25692      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25693      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25694      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25695      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25696      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25697      * })
25698      */
25699     // private
25700     
25701     meterWidth: 300,
25702     errorMsg :'',    
25703     errors: false,
25704     imageRoot: '/',
25705     /**
25706      * @cfg {String/Object} Label for the strength meter (defaults to
25707      * 'Password strength:')
25708      */
25709     // private
25710     meterLabel: '',
25711     /**
25712      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25713      * ['Weak', 'Medium', 'Strong'])
25714      */
25715     // private    
25716     pwdStrengths: false,    
25717     // private
25718     strength: 0,
25719     // private
25720     _lastPwd: null,
25721     // private
25722     kCapitalLetter: 0,
25723     kSmallLetter: 1,
25724     kDigit: 2,
25725     kPunctuation: 3,
25726     
25727     insecure: false,
25728     // private
25729     initEvents: function ()
25730     {
25731         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25732
25733         if (this.el.is('input[type=password]') && Roo.isSafari) {
25734             this.el.on('keydown', this.SafariOnKeyDown, this);
25735         }
25736
25737         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25738     },
25739     // private
25740     onRender: function (ct, position)
25741     {
25742         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25743         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25744         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25745
25746         this.trigger.createChild({
25747                    cn: [
25748                     {
25749                     //id: 'PwdMeter',
25750                     tag: 'div',
25751                     cls: 'roo-password-meter-grey col-xs-12',
25752                     style: {
25753                         //width: 0,
25754                         //width: this.meterWidth + 'px'                                                
25755                         }
25756                     },
25757                     {                            
25758                          cls: 'roo-password-meter-text'                          
25759                     }
25760                 ]            
25761         });
25762
25763          
25764         if (this.hideTrigger) {
25765             this.trigger.setDisplayed(false);
25766         }
25767         this.setSize(this.width || '', this.height || '');
25768     },
25769     // private
25770     onDestroy: function ()
25771     {
25772         if (this.trigger) {
25773             this.trigger.removeAllListeners();
25774             this.trigger.remove();
25775         }
25776         if (this.wrap) {
25777             this.wrap.remove();
25778         }
25779         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25780     },
25781     // private
25782     checkStrength: function ()
25783     {
25784         var pwd = this.inputEl().getValue();
25785         if (pwd == this._lastPwd) {
25786             return;
25787         }
25788
25789         var strength;
25790         if (this.ClientSideStrongPassword(pwd)) {
25791             strength = 3;
25792         } else if (this.ClientSideMediumPassword(pwd)) {
25793             strength = 2;
25794         } else if (this.ClientSideWeakPassword(pwd)) {
25795             strength = 1;
25796         } else {
25797             strength = 0;
25798         }
25799         
25800         Roo.log('strength1: ' + strength);
25801         
25802         //var pm = this.trigger.child('div/div/div').dom;
25803         var pm = this.trigger.child('div/div');
25804         pm.removeClass(this.meterClass);
25805         pm.addClass(this.meterClass[strength]);
25806                 
25807         
25808         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25809                 
25810         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25811         
25812         this._lastPwd = pwd;
25813     },
25814     reset: function ()
25815     {
25816         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25817         
25818         this._lastPwd = '';
25819         
25820         var pm = this.trigger.child('div/div');
25821         pm.removeClass(this.meterClass);
25822         pm.addClass('roo-password-meter-grey');        
25823         
25824         
25825         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25826         
25827         pt.innerHTML = '';
25828         this.inputEl().dom.type='password';
25829     },
25830     // private
25831     validateValue: function (value)
25832     {
25833         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25834             return false;
25835         }
25836         if (value.length == 0) {
25837             if (this.allowBlank) {
25838                 this.clearInvalid();
25839                 return true;
25840             }
25841
25842             this.markInvalid(this.errors.PwdEmpty);
25843             this.errorMsg = this.errors.PwdEmpty;
25844             return false;
25845         }
25846         
25847         if(this.insecure){
25848             return true;
25849         }
25850         
25851         if (!value.match(/[\x21-\x7e]+/)) {
25852             this.markInvalid(this.errors.PwdBadChar);
25853             this.errorMsg = this.errors.PwdBadChar;
25854             return false;
25855         }
25856         if (value.length < 6) {
25857             this.markInvalid(this.errors.PwdShort);
25858             this.errorMsg = this.errors.PwdShort;
25859             return false;
25860         }
25861         if (value.length > 16) {
25862             this.markInvalid(this.errors.PwdLong);
25863             this.errorMsg = this.errors.PwdLong;
25864             return false;
25865         }
25866         var strength;
25867         if (this.ClientSideStrongPassword(value)) {
25868             strength = 3;
25869         } else if (this.ClientSideMediumPassword(value)) {
25870             strength = 2;
25871         } else if (this.ClientSideWeakPassword(value)) {
25872             strength = 1;
25873         } else {
25874             strength = 0;
25875         }
25876
25877         
25878         if (strength < 2) {
25879             //this.markInvalid(this.errors.TooWeak);
25880             this.errorMsg = this.errors.TooWeak;
25881             //return false;
25882         }
25883         
25884         
25885         console.log('strength2: ' + strength);
25886         
25887         //var pm = this.trigger.child('div/div/div').dom;
25888         
25889         var pm = this.trigger.child('div/div');
25890         pm.removeClass(this.meterClass);
25891         pm.addClass(this.meterClass[strength]);
25892                 
25893         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25894                 
25895         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25896         
25897         this.errorMsg = ''; 
25898         return true;
25899     },
25900     // private
25901     CharacterSetChecks: function (type)
25902     {
25903         this.type = type;
25904         this.fResult = false;
25905     },
25906     // private
25907     isctype: function (character, type)
25908     {
25909         switch (type) {  
25910             case this.kCapitalLetter:
25911                 if (character >= 'A' && character <= 'Z') {
25912                     return true;
25913                 }
25914                 break;
25915             
25916             case this.kSmallLetter:
25917                 if (character >= 'a' && character <= 'z') {
25918                     return true;
25919                 }
25920                 break;
25921             
25922             case this.kDigit:
25923                 if (character >= '0' && character <= '9') {
25924                     return true;
25925                 }
25926                 break;
25927             
25928             case this.kPunctuation:
25929                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25930                     return true;
25931                 }
25932                 break;
25933             
25934             default:
25935                 return false;
25936         }
25937
25938     },
25939     // private
25940     IsLongEnough: function (pwd, size)
25941     {
25942         return !(pwd == null || isNaN(size) || pwd.length < size);
25943     },
25944     // private
25945     SpansEnoughCharacterSets: function (word, nb)
25946     {
25947         if (!this.IsLongEnough(word, nb))
25948         {
25949             return false;
25950         }
25951
25952         var characterSetChecks = new Array(
25953             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25954             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25955         );
25956         
25957         for (var index = 0; index < word.length; ++index) {
25958             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25959                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25960                     characterSetChecks[nCharSet].fResult = true;
25961                     break;
25962                 }
25963             }
25964         }
25965
25966         var nCharSets = 0;
25967         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25968             if (characterSetChecks[nCharSet].fResult) {
25969                 ++nCharSets;
25970             }
25971         }
25972
25973         if (nCharSets < nb) {
25974             return false;
25975         }
25976         return true;
25977     },
25978     // private
25979     ClientSideStrongPassword: function (pwd)
25980     {
25981         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25982     },
25983     // private
25984     ClientSideMediumPassword: function (pwd)
25985     {
25986         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25987     },
25988     // private
25989     ClientSideWeakPassword: function (pwd)
25990     {
25991         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25992     }
25993           
25994 });
25995 Roo.htmleditor = {};
25996  
25997 /**
25998  * @class Roo.htmleditor.Filter
25999  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26000  * @cfg {DomElement} node The node to iterate and filter
26001  * @cfg {boolean|String|Array} tag Tags to replace 
26002  * @constructor
26003  * Create a new Filter.
26004  * @param {Object} config Configuration options
26005  */
26006
26007
26008
26009 Roo.htmleditor.Filter = function(cfg) {
26010     Roo.apply(this.cfg);
26011     // this does not actually call walk as it's really just a abstract class
26012 }
26013
26014
26015 Roo.htmleditor.Filter.prototype = {
26016     
26017     node: false,
26018     
26019     tag: false,
26020
26021     // overrride to do replace comments.
26022     replaceComment : false,
26023     
26024     // overrride to do replace or do stuff with tags..
26025     replaceTag : false,
26026     
26027     walk : function(dom)
26028     {
26029         Roo.each( Array.from(dom.childNodes), function( e ) {
26030             switch(true) {
26031                 
26032                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26033                     this.replaceComment(e);
26034                     return;
26035                 
26036                 case e.nodeType != 1: //not a node.
26037                     return;
26038                 
26039                 case this.tag === true: // everything
26040                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26041                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26042                     if (this.replaceTag && false === this.replaceTag(e)) {
26043                         return;
26044                     }
26045                     if (e.hasChildNodes()) {
26046                         this.walk(e);
26047                     }
26048                     return;
26049                 
26050                 default:    // tags .. that do not match.
26051                     if (e.hasChildNodes()) {
26052                         this.walk(e);
26053                     }
26054             }
26055             
26056         }, this);
26057         
26058     }
26059 }; 
26060
26061 /**
26062  * @class Roo.htmleditor.FilterAttributes
26063  * clean attributes and  styles including http:// etc.. in attribute
26064  * @constructor
26065 * Run a new Attribute Filter
26066 * @param {Object} config Configuration options
26067  */
26068 Roo.htmleditor.FilterAttributes = function(cfg)
26069 {
26070     Roo.apply(this, cfg);
26071     this.attrib_black = this.attrib_black || [];
26072     this.attrib_white = this.attrib_white || [];
26073
26074     this.attrib_clean = this.attrib_clean || [];
26075     this.style_white = this.style_white || [];
26076     this.style_black = this.style_black || [];
26077     this.walk(cfg.node);
26078 }
26079
26080 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26081 {
26082     tag: true, // all tags
26083     
26084     attrib_black : false, // array
26085     attrib_clean : false,
26086     attrib_white : false,
26087
26088     style_white : false,
26089     style_black : false,
26090      
26091      
26092     replaceTag : function(node)
26093     {
26094         if (!node.attributes || !node.attributes.length) {
26095             return true;
26096         }
26097         
26098         for (var i = node.attributes.length-1; i > -1 ; i--) {
26099             var a = node.attributes[i];
26100             //console.log(a);
26101             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26102                 node.removeAttribute(a.name);
26103                 continue;
26104             }
26105             
26106             
26107             
26108             if (a.name.toLowerCase().substr(0,2)=='on')  {
26109                 node.removeAttribute(a.name);
26110                 continue;
26111             }
26112             
26113             
26114             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26115                 node.removeAttribute(a.name);
26116                 continue;
26117             }
26118             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26119                 this.cleanAttr(node,a.name,a.value); // fixme..
26120                 continue;
26121             }
26122             if (a.name == 'style') {
26123                 this.cleanStyle(node,a.name,a.value);
26124                 continue;
26125             }
26126             /// clean up MS crap..
26127             // tecnically this should be a list of valid class'es..
26128             
26129             
26130             if (a.name == 'class') {
26131                 if (a.value.match(/^Mso/)) {
26132                     node.removeAttribute('class');
26133                 }
26134                 
26135                 if (a.value.match(/^body$/)) {
26136                     node.removeAttribute('class');
26137                 }
26138                 continue;
26139             }
26140             
26141             
26142             // style cleanup!?
26143             // class cleanup?
26144             
26145         }
26146         return true; // clean children
26147     },
26148         
26149     cleanAttr: function(node, n,v)
26150     {
26151         
26152         if (v.match(/^\./) || v.match(/^\//)) {
26153             return;
26154         }
26155         if (v.match(/^(http|https):\/\//)
26156             || v.match(/^mailto:/) 
26157             || v.match(/^ftp:/)
26158             || v.match(/^data:/)
26159             ) {
26160             return;
26161         }
26162         if (v.match(/^#/)) {
26163             return;
26164         }
26165         if (v.match(/^\{/)) { // allow template editing.
26166             return;
26167         }
26168 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26169         node.removeAttribute(n);
26170         
26171     },
26172     cleanStyle : function(node,  n,v)
26173     {
26174         if (v.match(/expression/)) { //XSS?? should we even bother..
26175             node.removeAttribute(n);
26176             return;
26177         }
26178         
26179         var parts = v.split(/;/);
26180         var clean = [];
26181         
26182         Roo.each(parts, function(p) {
26183             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26184             if (!p.length) {
26185                 return true;
26186             }
26187             var l = p.split(':').shift().replace(/\s+/g,'');
26188             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26189             
26190             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26191                 return true;
26192             }
26193             //Roo.log()
26194             // only allow 'c whitelisted system attributes'
26195             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26196                 return true;
26197             }
26198             
26199             
26200             clean.push(p);
26201             return true;
26202         },this);
26203         if (clean.length) { 
26204             node.setAttribute(n, clean.join(';'));
26205         } else {
26206             node.removeAttribute(n);
26207         }
26208         
26209     }
26210         
26211         
26212         
26213     
26214 });/**
26215  * @class Roo.htmleditor.FilterBlack
26216  * remove blacklisted elements.
26217  * @constructor
26218  * Run a new Blacklisted Filter
26219  * @param {Object} config Configuration options
26220  */
26221
26222 Roo.htmleditor.FilterBlack = function(cfg)
26223 {
26224     Roo.apply(this, cfg);
26225     this.walk(cfg.node);
26226 }
26227
26228 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26229 {
26230     tag : true, // all elements.
26231    
26232     replaceTag : function(n)
26233     {
26234         n.parentNode.removeChild(n);
26235     }
26236 });
26237 /**
26238  * @class Roo.htmleditor.FilterComment
26239  * remove comments.
26240  * @constructor
26241 * Run a new Comments Filter
26242 * @param {Object} config Configuration options
26243  */
26244 Roo.htmleditor.FilterComment = function(cfg)
26245 {
26246     this.walk(cfg.node);
26247 }
26248
26249 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26250 {
26251   
26252     replaceComment : function(n)
26253     {
26254         n.parentNode.removeChild(n);
26255     }
26256 });/**
26257  * @class Roo.htmleditor.FilterKeepChildren
26258  * remove tags but keep children
26259  * @constructor
26260  * Run a new Keep Children Filter
26261  * @param {Object} config Configuration options
26262  */
26263
26264 Roo.htmleditor.FilterKeepChildren = function(cfg)
26265 {
26266     Roo.apply(this, cfg);
26267     if (this.tag === false) {
26268         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26269     }
26270     this.walk(cfg.node);
26271 }
26272
26273 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26274 {
26275     
26276   
26277     replaceTag : function(node)
26278     {
26279         // walk children...
26280         //Roo.log(node);
26281         var ar = Array.from(node.childNodes);
26282         //remove first..
26283         for (var i = 0; i < ar.length; i++) {
26284             if (ar[i].nodeType == 1) {
26285                 if (
26286                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
26287                     || // array and it matches
26288                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
26289                 ) {
26290                     this.replaceTag(ar[i]); // child is blacklisted as well...
26291                     continue;
26292                 }
26293             }
26294         }  
26295         ar = Array.from(node.childNodes);
26296         for (var i = 0; i < ar.length; i++) {
26297          
26298             node.removeChild(ar[i]);
26299             // what if we need to walk these???
26300             node.parentNode.insertBefore(ar[i], node);
26301             if (this.tag !== false) {
26302                 this.walk(ar[i]);
26303                 
26304             }
26305         }
26306         node.parentNode.removeChild(node);
26307         return false; // don't walk children
26308         
26309         
26310     }
26311 });/**
26312  * @class Roo.htmleditor.FilterParagraph
26313  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26314  * like on 'push' to remove the <p> tags and replace them with line breaks.
26315  * @constructor
26316  * Run a new Paragraph Filter
26317  * @param {Object} config Configuration options
26318  */
26319
26320 Roo.htmleditor.FilterParagraph = function(cfg)
26321 {
26322     // no need to apply config.
26323     this.walk(cfg.node);
26324 }
26325
26326 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26327 {
26328     
26329      
26330     tag : 'P',
26331     
26332      
26333     replaceTag : function(node)
26334     {
26335         
26336         if (node.childNodes.length == 1 &&
26337             node.childNodes[0].nodeType == 3 &&
26338             node.childNodes[0].textContent.trim().length < 1
26339             ) {
26340             // remove and replace with '<BR>';
26341             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26342             return false; // no need to walk..
26343         }
26344         var ar = Array.from(node.childNodes);
26345         for (var i = 0; i < ar.length; i++) {
26346             node.removeChild(ar[i]);
26347             // what if we need to walk these???
26348             node.parentNode.insertBefore(ar[i], node);
26349         }
26350         // now what about this?
26351         // <p> &nbsp; </p>
26352         
26353         // double BR.
26354         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26355         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26356         node.parentNode.removeChild(node);
26357         
26358         return false;
26359
26360     }
26361     
26362 });/**
26363  * @class Roo.htmleditor.FilterSpan
26364  * filter span's with no attributes out..
26365  * @constructor
26366  * Run a new Span Filter
26367  * @param {Object} config Configuration options
26368  */
26369
26370 Roo.htmleditor.FilterSpan = function(cfg)
26371 {
26372     // no need to apply config.
26373     this.walk(cfg.node);
26374 }
26375
26376 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26377 {
26378      
26379     tag : 'SPAN',
26380      
26381  
26382     replaceTag : function(node)
26383     {
26384         if (node.attributes && node.attributes.length > 0) {
26385             return true; // walk if there are any.
26386         }
26387         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26388         return false;
26389      
26390     }
26391     
26392 });/**
26393  * @class Roo.htmleditor.FilterTableWidth
26394   try and remove table width data - as that frequently messes up other stuff.
26395  * 
26396  *      was cleanTableWidths.
26397  *
26398  * Quite often pasting from word etc.. results in tables with column and widths.
26399  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26400  *
26401  * @constructor
26402  * Run a new Table Filter
26403  * @param {Object} config Configuration options
26404  */
26405
26406 Roo.htmleditor.FilterTableWidth = function(cfg)
26407 {
26408     // no need to apply config.
26409     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26410     this.walk(cfg.node);
26411 }
26412
26413 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26414 {
26415      
26416      
26417     
26418     replaceTag: function(node) {
26419         
26420         
26421       
26422         if (node.hasAttribute('width')) {
26423             node.removeAttribute('width');
26424         }
26425         
26426          
26427         if (node.hasAttribute("style")) {
26428             // pretty basic...
26429             
26430             var styles = node.getAttribute("style").split(";");
26431             var nstyle = [];
26432             Roo.each(styles, function(s) {
26433                 if (!s.match(/:/)) {
26434                     return;
26435                 }
26436                 var kv = s.split(":");
26437                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26438                     return;
26439                 }
26440                 // what ever is left... we allow.
26441                 nstyle.push(s);
26442             });
26443             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26444             if (!nstyle.length) {
26445                 node.removeAttribute('style');
26446             }
26447         }
26448         
26449         return true; // continue doing children..
26450     }
26451 });/**
26452  * @class Roo.htmleditor.FilterWord
26453  * try and clean up all the mess that Word generates.
26454  * 
26455  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26456  
26457  * @constructor
26458  * Run a new Span Filter
26459  * @param {Object} config Configuration options
26460  */
26461
26462 Roo.htmleditor.FilterWord = function(cfg)
26463 {
26464     // no need to apply config.
26465     this.replaceDocBullets(cfg.node);
26466     
26467    // this.walk(cfg.node);
26468     
26469     
26470 }
26471
26472 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26473 {
26474     tag: true,
26475      
26476     
26477     /**
26478      * Clean up MS wordisms...
26479      */
26480     replaceTag : function(node)
26481     {
26482          
26483         // no idea what this does - span with text, replaceds with just text.
26484         if(
26485                 node.nodeName == 'SPAN' &&
26486                 !node.hasAttributes() &&
26487                 node.childNodes.length == 1 &&
26488                 node.firstChild.nodeName == "#text"  
26489         ) {
26490             var textNode = node.firstChild;
26491             node.removeChild(textNode);
26492             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26493                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26494             }
26495             node.parentNode.insertBefore(textNode, node);
26496             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26497                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26498             }
26499             
26500             node.parentNode.removeChild(node);
26501             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26502         }
26503         
26504    
26505         
26506         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26507             node.parentNode.removeChild(node);
26508             return false; // dont do chidlren
26509         }
26510         //Roo.log(node.tagName);
26511         // remove - but keep children..
26512         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26513             //Roo.log('-- removed');
26514             while (node.childNodes.length) {
26515                 var cn = node.childNodes[0];
26516                 node.removeChild(cn);
26517                 node.parentNode.insertBefore(cn, node);
26518                 // move node to parent - and clean it..
26519                 if (cn.nodeType == 1) {
26520                     this.replaceTag(cn);
26521                 }
26522                 
26523             }
26524             node.parentNode.removeChild(node);
26525             /// no need to iterate chidlren = it's got none..
26526             //this.iterateChildren(node, this.cleanWord);
26527             return false; // no need to iterate children.
26528         }
26529         // clean styles
26530         if (node.className.length) {
26531             
26532             var cn = node.className.split(/\W+/);
26533             var cna = [];
26534             Roo.each(cn, function(cls) {
26535                 if (cls.match(/Mso[a-zA-Z]+/)) {
26536                     return;
26537                 }
26538                 cna.push(cls);
26539             });
26540             node.className = cna.length ? cna.join(' ') : '';
26541             if (!cna.length) {
26542                 node.removeAttribute("class");
26543             }
26544         }
26545         
26546         if (node.hasAttribute("lang")) {
26547             node.removeAttribute("lang");
26548         }
26549         
26550         if (node.hasAttribute("style")) {
26551             
26552             var styles = node.getAttribute("style").split(";");
26553             var nstyle = [];
26554             Roo.each(styles, function(s) {
26555                 if (!s.match(/:/)) {
26556                     return;
26557                 }
26558                 var kv = s.split(":");
26559                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26560                     return;
26561                 }
26562                 // what ever is left... we allow.
26563                 nstyle.push(s);
26564             });
26565             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26566             if (!nstyle.length) {
26567                 node.removeAttribute('style');
26568             }
26569         }
26570         return true; // do children
26571         
26572         
26573         
26574     },
26575     
26576     styleToObject: function(node)
26577     {
26578         var styles = (node.getAttribute("style") || '').split(";");
26579         var ret = {};
26580         Roo.each(styles, function(s) {
26581             if (!s.match(/:/)) {
26582                 return;
26583             }
26584             var kv = s.split(":");
26585              
26586             // what ever is left... we allow.
26587             ret[kv[0]] = kv[1];
26588         });
26589         return ret;
26590     },
26591     
26592     
26593     replaceDocBullets : function(doc)
26594     {
26595         // this is a bit odd - but it appears some indents use ql-indent-1
26596         
26597         var listpara = doc.getElementsByClassName('ql-indent-1');
26598         while(listpara.length) {
26599             this.replaceDocBullet(listpara.item(0));
26600         }
26601         
26602         var listpara = doc.getElementsByClassName('MsoListParagraph');
26603         while(listpara.length) {
26604             this.replaceDocBullet(listpara.item(0));
26605         }
26606     },
26607     
26608     replaceDocBullet : function(p)
26609     {
26610         // gather all the siblings.
26611         var ns = p,
26612             parent = p.parentNode,
26613             doc = parent.ownerDocument,
26614             items = []; 
26615         while (ns) {
26616             if (ns.nodeType != 1) {
26617                 ns = ns.nextSibling;
26618                 continue;
26619             }
26620             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26621                 break;
26622             }
26623             items.push(ns);
26624             ns = ns.nextSibling;
26625             
26626         }
26627         var ul = parent.ownerDocument.createElement('ul'); // what about number lists...
26628         parent.insertBefore(ul, p);
26629         var lvl = 0;
26630         var stack = [ ul ];
26631         var last_li = false;
26632         items.forEach(function(n) {
26633             //Roo.log("got innertHMLT=" + n.innerHTML);
26634             
26635             var spans = n.getElementsByTagName('span');
26636             if (!spans.length) {
26637                 //Roo.log("No spans found");
26638
26639                 parent.removeChild(n);
26640                 return; // skip it...
26641             }
26642            
26643                 
26644             
26645             var style = {};
26646             for(var i = 0; i < spans.length; i++) {
26647             
26648                 style = this.styleToObject(spans[i]);
26649                 if (typeof(style['mso-list']) == 'undefined') {
26650                     continue;
26651                 }
26652                 
26653                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26654                 break;
26655             }
26656             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26657             style = this.styleToObject(n); // mo-list is from the parent node.
26658             if (typeof(style['mso-list']) == 'undefined') {
26659                 //Roo.log("parent is missing level");
26660                 parent.removeChild(n);
26661                 return;
26662             }
26663             
26664             var nlvl = (style['mso-list'].split(' ')[1].replace(/level/,'') *1) - 1;
26665             if (nlvl > lvl) {
26666                 //new indent
26667                 var nul = doc.createElement('ul'); // what about number lists...
26668                 last_li.appendChild(nul);
26669                 stack[nlvl] = nul;
26670             }
26671             lvl = nlvl;
26672             
26673             var nli = stack[nlvl].appendChild(doc.createElement('li'));
26674             last_li = nli;
26675             nli.innerHTML = n.innerHTML;
26676             //Roo.log("innerHTML = " + n.innerHTML);
26677             parent.removeChild(n);
26678             
26679             // copy children of p into nli
26680             /*while(n.firstChild) {
26681                 var fc = n.firstChild;
26682                 n.removeChild(fc);
26683                 nli.appendChild(fc);
26684             }*/
26685              
26686             
26687         },this);
26688         
26689         
26690         
26691         
26692     }
26693     
26694     
26695     
26696 });
26697 /**
26698  * @class Roo.htmleditor.FilterStyleToTag
26699  * part of the word stuff... - certain 'styles' should be converted to tags.
26700  * eg.
26701  *   font-weight: bold -> bold
26702  *   ?? super / subscrit etc..
26703  * 
26704  * @constructor
26705 * Run a new style to tag filter.
26706 * @param {Object} config Configuration options
26707  */
26708 Roo.htmleditor.FilterStyleToTag = function(cfg)
26709 {
26710     
26711     this.tags = {
26712         B  : [ 'fontWeight' , 'bold'],
26713         I :  [ 'fontStyle' , 'italic'],
26714         //pre :  [ 'font-style' , 'italic'],
26715         // h1.. h6 ?? font-size?
26716         SUP : [ 'verticalAlign' , 'super' ],
26717         SUB : [ 'verticalAlign' , 'sub' ]
26718         
26719         
26720     };
26721     
26722     Roo.apply(this, cfg);
26723      
26724     
26725     this.walk(cfg.node);
26726     
26727     
26728     
26729 }
26730
26731
26732 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26733 {
26734     tag: true, // all tags
26735     
26736     tags : false,
26737     
26738     
26739     replaceTag : function(node)
26740     {
26741         
26742         
26743         if (node.getAttribute("style") === null) {
26744             return true;
26745         }
26746         var inject = [];
26747         for (var k in this.tags) {
26748             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26749                 inject.push(k);
26750                 node.style.removeProperty(this.tags[k][0]);
26751             }
26752         }
26753         if (!inject.length) {
26754             return true; 
26755         }
26756         var cn = Array.from(node.childNodes);
26757         var nn = node;
26758         Roo.each(inject, function(t) {
26759             var nc = node.ownerDocument.createElement(t);
26760             nn.appendChild(nc);
26761             nn = nc;
26762         });
26763         for(var i = 0;i < cn.length;cn++) {
26764             node.removeChild(cn[i]);
26765             nn.appendChild(cn[i]);
26766         }
26767         return true /// iterate thru
26768     }
26769     
26770 })/**
26771  * @class Roo.htmleditor.FilterLongBr
26772  * BR/BR/BR - keep a maximum of 2...
26773  * @constructor
26774  * Run a new Long BR Filter
26775  * @param {Object} config Configuration options
26776  */
26777
26778 Roo.htmleditor.FilterLongBr = function(cfg)
26779 {
26780     // no need to apply config.
26781     this.walk(cfg.node);
26782 }
26783
26784 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26785 {
26786     
26787      
26788     tag : 'BR',
26789     
26790      
26791     replaceTag : function(node)
26792     {
26793         
26794         var ps = node.nextSibling;
26795         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26796             ps = ps.nextSibling;
26797         }
26798         
26799         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
26800             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26801             return false;
26802         }
26803         
26804         if (!ps || ps.nodeType != 1) {
26805             return false;
26806         }
26807         
26808         if (!ps || ps.tagName != 'BR') {
26809            
26810             return false;
26811         }
26812         
26813         
26814         
26815         
26816         
26817         if (!node.previousSibling) {
26818             return false;
26819         }
26820         var ps = node.previousSibling;
26821         
26822         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26823             ps = ps.previousSibling;
26824         }
26825         if (!ps || ps.nodeType != 1) {
26826             return false;
26827         }
26828         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
26829         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
26830             return false;
26831         }
26832         
26833         node.parentNode.removeChild(node); // remove me...
26834         
26835         return false; // no need to do children
26836
26837     }
26838     
26839 }); 
26840
26841 /**
26842  * @class Roo.htmleditor.FilterBlock
26843  * removes id / data-block and contenteditable that are associated with blocks
26844  * usage should be done on a cloned copy of the dom
26845  * @constructor
26846 * Run a new Attribute Filter { node : xxxx }}
26847 * @param {Object} config Configuration options
26848  */
26849 Roo.htmleditor.FilterBlock = function(cfg)
26850 {
26851     Roo.apply(this, cfg);
26852     var qa = cfg.node.querySelectorAll;
26853     this.removeAttributes('data-block');
26854     this.removeAttributes('contenteditable');
26855     this.removeAttributes('id');
26856     
26857 }
26858
26859 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
26860 {
26861     node: true, // all tags
26862      
26863      
26864     removeAttributes : function(attr)
26865     {
26866         var ar = this.node.querySelectorAll('*[' + attr + ']');
26867         for (var i =0;i<ar.length;i++) {
26868             ar[i].removeAttribute(attr);
26869         }
26870     }
26871         
26872         
26873         
26874     
26875 });
26876 /**
26877  * @class Roo.htmleditor.KeyEnter
26878  * Handle Enter press..
26879  * @cfg {Roo.HtmlEditorCore} core the editor.
26880  * @constructor
26881  * Create a new Filter.
26882  * @param {Object} config Configuration options
26883  */
26884
26885
26886
26887
26888
26889 Roo.htmleditor.KeyEnter = function(cfg) {
26890     Roo.apply(this, cfg);
26891     // this does not actually call walk as it's really just a abstract class
26892  
26893     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
26894 }
26895
26896 //Roo.htmleditor.KeyEnter.i = 0;
26897
26898
26899 Roo.htmleditor.KeyEnter.prototype = {
26900     
26901     core : false,
26902     
26903     keypress : function(e)
26904     {
26905         if (e.charCode != 13 && e.charCode != 10) {
26906             Roo.log([e.charCode,e]);
26907             return true;
26908         }
26909         e.preventDefault();
26910         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
26911         var doc = this.core.doc;
26912           //add a new line
26913        
26914     
26915         var sel = this.core.getSelection();
26916         var range = sel.getRangeAt(0);
26917         var n = range.commonAncestorContainer;
26918         var pc = range.closest([ 'ol', 'ul']);
26919         var pli = range.closest('li');
26920         if (!pc || e.ctrlKey) {
26921             sel.insertNode('br', 'after'); 
26922          
26923             this.core.undoManager.addEvent();
26924             this.core.fireEditorEvent(e);
26925             return false;
26926         }
26927         
26928         // deal with <li> insetion
26929         if (pli.innerText.trim() == '' &&
26930             pli.previousSibling &&
26931             pli.previousSibling.nodeName == 'LI' &&
26932             pli.previousSibling.innerText.trim() ==  '') {
26933             pli.parentNode.removeChild(pli.previousSibling);
26934             sel.cursorAfter(pc);
26935             this.core.undoManager.addEvent();
26936             this.core.fireEditorEvent(e);
26937             return false;
26938         }
26939     
26940         var li = doc.createElement('LI');
26941         li.innerHTML = '&nbsp;';
26942         if (!pli || !pli.firstSibling) {
26943             pc.appendChild(li);
26944         } else {
26945             pli.parentNode.insertBefore(li, pli.firstSibling);
26946         }
26947         sel.cursorText (li.firstChild);
26948       
26949         this.core.undoManager.addEvent();
26950         this.core.fireEditorEvent(e);
26951
26952         return false;
26953         
26954     
26955         
26956         
26957          
26958     }
26959 };
26960      
26961 /**
26962  * @class Roo.htmleditor.Block
26963  * Base class for html editor blocks - do not use it directly .. extend it..
26964  * @cfg {DomElement} node The node to apply stuff to.
26965  * @cfg {String} friendly_name the name that appears in the context bar about this block
26966  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
26967  
26968  * @constructor
26969  * Create a new Filter.
26970  * @param {Object} config Configuration options
26971  */
26972
26973 Roo.htmleditor.Block  = function(cfg)
26974 {
26975     // do nothing .. should not be called really.
26976 }
26977 /**
26978  * factory method to get the block from an element (using cache if necessary)
26979  * @static
26980  * @param {HtmlElement} the dom element
26981  */
26982 Roo.htmleditor.Block.factory = function(node)
26983 {
26984     var cc = Roo.htmleditor.Block.cache;
26985     var id = Roo.get(node).id;
26986     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
26987         Roo.htmleditor.Block.cache[id].readElement(node);
26988         return Roo.htmleditor.Block.cache[id];
26989     }
26990     var db  = node.getAttribute('data-block');
26991     if (!db) {
26992         db = node.nodeName.toLowerCase().toUpperCaseFirst();
26993     }
26994     var cls = Roo.htmleditor['Block' + db];
26995     if (typeof(cls) == 'undefined') {
26996         //Roo.log(node.getAttribute('data-block'));
26997         Roo.log("OOps missing block : " + 'Block' + db);
26998         return false;
26999     }
27000     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
27001     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
27002 };
27003
27004 /**
27005  * initalize all Elements from content that are 'blockable'
27006  * @static
27007  * @param the body element
27008  */
27009 Roo.htmleditor.Block.initAll = function(body, type)
27010 {
27011     if (typeof(type) == 'undefined') {
27012         var ia = Roo.htmleditor.Block.initAll;
27013         ia(body,'table');
27014         ia(body,'td');
27015         ia(body,'figure');
27016         return;
27017     }
27018     Roo.each(Roo.get(body).query(type), function(e) {
27019         Roo.htmleditor.Block.factory(e);    
27020     },this);
27021 };
27022 // question goes here... do we need to clear out this cache sometimes?
27023 // or show we make it relivant to the htmleditor.
27024 Roo.htmleditor.Block.cache = {};
27025
27026 Roo.htmleditor.Block.prototype = {
27027     
27028     node : false,
27029     
27030      // used by context menu
27031     friendly_name : 'Based Block',
27032     
27033     // text for button to delete this element
27034     deleteTitle : false,
27035     
27036     context : false,
27037     /**
27038      * Update a node with values from this object
27039      * @param {DomElement} node
27040      */
27041     updateElement : function(node)
27042     {
27043         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
27044     },
27045      /**
27046      * convert to plain HTML for calling insertAtCursor..
27047      */
27048     toHTML : function()
27049     {
27050         return Roo.DomHelper.markup(this.toObject());
27051     },
27052     /**
27053      * used by readEleemnt to extract data from a node
27054      * may need improving as it's pretty basic
27055      
27056      * @param {DomElement} node
27057      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
27058      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
27059      * @param {String} style the style property - eg. text-align
27060      */
27061     getVal : function(node, tag, attr, style)
27062     {
27063         var n = node;
27064         if (tag !== true && n.tagName != tag.toUpperCase()) {
27065             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
27066             // but kiss for now.
27067             n = node.getElementsByTagName(tag).item(0);
27068         }
27069         if (!n) {
27070             return '';
27071         }
27072         if (attr === false) {
27073             return n;
27074         }
27075         if (attr == 'html') {
27076             return n.innerHTML;
27077         }
27078         if (attr == 'style') {
27079             return n.style[style]; 
27080         }
27081         
27082         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
27083             
27084     },
27085     /**
27086      * create a DomHelper friendly object - for use with 
27087      * Roo.DomHelper.markup / overwrite / etc..
27088      * (override this)
27089      */
27090     toObject : function()
27091     {
27092         return {};
27093     },
27094       /**
27095      * Read a node that has a 'data-block' property - and extract the values from it.
27096      * @param {DomElement} node - the node
27097      */
27098     readElement : function(node)
27099     {
27100         
27101     } 
27102     
27103     
27104 };
27105
27106  
27107
27108 /**
27109  * @class Roo.htmleditor.BlockFigure
27110  * Block that has an image and a figcaption
27111  * @cfg {String} image_src the url for the image
27112  * @cfg {String} align (left|right) alignment for the block default left
27113  * @cfg {String} caption the text to appear below  (and in the alt tag)
27114  * @cfg {String} caption_display (block|none) display or not the caption
27115  * @cfg {String|number} image_width the width of the image number or %?
27116  * @cfg {String|number} image_height the height of the image number or %?
27117  * 
27118  * @constructor
27119  * Create a new Filter.
27120  * @param {Object} config Configuration options
27121  */
27122
27123 Roo.htmleditor.BlockFigure = function(cfg)
27124 {
27125     if (cfg.node) {
27126         this.readElement(cfg.node);
27127         this.updateElement(cfg.node);
27128     }
27129     Roo.apply(this, cfg);
27130 }
27131 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
27132  
27133     
27134     // setable values.
27135     image_src: '',
27136     align: 'center',
27137     caption : '',
27138     caption_display : 'block',
27139     width : '100%',
27140     cls : '',
27141     href: '',
27142     video_url : '',
27143     
27144     // margin: '2%', not used
27145     
27146     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
27147
27148     
27149     // used by context menu
27150     friendly_name : 'Image with caption',
27151     deleteTitle : "Delete Image and Caption",
27152     
27153     contextMenu : function(toolbar)
27154     {
27155         
27156         var block = function() {
27157             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27158         };
27159         
27160         
27161         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27162         
27163         var syncValue = toolbar.editorcore.syncValue;
27164         
27165         var fields = {};
27166         
27167         return [
27168              {
27169                 xtype : 'TextItem',
27170                 text : "Source: ",
27171                 xns : rooui.Toolbar  //Boostrap?
27172             },
27173             {
27174                 xtype : 'Button',
27175                 text: 'Change Image URL',
27176                  
27177                 listeners : {
27178                     click: function (btn, state)
27179                     {
27180                         var b = block();
27181                         
27182                         Roo.MessageBox.show({
27183                             title : "Image Source URL",
27184                             msg : "Enter the url for the image",
27185                             buttons: Roo.MessageBox.OKCANCEL,
27186                             fn: function(btn, val){
27187                                 if (btn != 'ok') {
27188                                     return;
27189                                 }
27190                                 b.image_src = val;
27191                                 b.updateElement();
27192                                 syncValue();
27193                                 toolbar.editorcore.onEditorEvent();
27194                             },
27195                             minWidth:250,
27196                             prompt:true,
27197                             //multiline: multiline,
27198                             modal : true,
27199                             value : b.image_src
27200                         });
27201                     }
27202                 },
27203                 xns : rooui.Toolbar
27204             },
27205          
27206             {
27207                 xtype : 'Button',
27208                 text: 'Change Link URL',
27209                  
27210                 listeners : {
27211                     click: function (btn, state)
27212                     {
27213                         var b = block();
27214                         
27215                         Roo.MessageBox.show({
27216                             title : "Link URL",
27217                             msg : "Enter the url for the link - leave blank to have no link",
27218                             buttons: Roo.MessageBox.OKCANCEL,
27219                             fn: function(btn, val){
27220                                 if (btn != 'ok') {
27221                                     return;
27222                                 }
27223                                 b.href = val;
27224                                 b.updateElement();
27225                                 syncValue();
27226                                 toolbar.editorcore.onEditorEvent();
27227                             },
27228                             minWidth:250,
27229                             prompt:true,
27230                             //multiline: multiline,
27231                             modal : true,
27232                             value : b.href
27233                         });
27234                     }
27235                 },
27236                 xns : rooui.Toolbar
27237             },
27238             {
27239                 xtype : 'Button',
27240                 text: 'Show Video URL',
27241                  
27242                 listeners : {
27243                     click: function (btn, state)
27244                     {
27245                         Roo.MessageBox.alert("Video URL",
27246                             block().video_url == '' ? 'This image is not linked ot a video' :
27247                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
27248                     }
27249                 },
27250                 xns : rooui.Toolbar
27251             },
27252             
27253             
27254             {
27255                 xtype : 'TextItem',
27256                 text : "Width: ",
27257                 xns : rooui.Toolbar  //Boostrap?
27258             },
27259             {
27260                 xtype : 'ComboBox',
27261                 allowBlank : false,
27262                 displayField : 'val',
27263                 editable : true,
27264                 listWidth : 100,
27265                 triggerAction : 'all',
27266                 typeAhead : true,
27267                 valueField : 'val',
27268                 width : 70,
27269                 name : 'width',
27270                 listeners : {
27271                     select : function (combo, r, index)
27272                     {
27273                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27274                         var b = block();
27275                         b.width = r.get('val');
27276                         b.updateElement();
27277                         syncValue();
27278                         toolbar.editorcore.onEditorEvent();
27279                     }
27280                 },
27281                 xns : rooui.form,
27282                 store : {
27283                     xtype : 'SimpleStore',
27284                     data : [
27285                         ['50%'],
27286                         ['80%'],
27287                         ['100%']
27288                     ],
27289                     fields : [ 'val'],
27290                     xns : Roo.data
27291                 }
27292             },
27293             {
27294                 xtype : 'TextItem',
27295                 text : "Align: ",
27296                 xns : rooui.Toolbar  //Boostrap?
27297             },
27298             {
27299                 xtype : 'ComboBox',
27300                 allowBlank : false,
27301                 displayField : 'val',
27302                 editable : true,
27303                 listWidth : 100,
27304                 triggerAction : 'all',
27305                 typeAhead : true,
27306                 valueField : 'val',
27307                 width : 70,
27308                 name : 'align',
27309                 listeners : {
27310                     select : function (combo, r, index)
27311                     {
27312                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27313                         var b = block();
27314                         b.align = r.get('val');
27315                         b.updateElement();
27316                         syncValue();
27317                         toolbar.editorcore.onEditorEvent();
27318                     }
27319                 },
27320                 xns : rooui.form,
27321                 store : {
27322                     xtype : 'SimpleStore',
27323                     data : [
27324                         ['left'],
27325                         ['right'],
27326                         ['center']
27327                     ],
27328                     fields : [ 'val'],
27329                     xns : Roo.data
27330                 }
27331             },
27332             
27333             
27334             {
27335                 xtype : 'Button',
27336                 text: 'Hide Caption',
27337                 name : 'caption_display',
27338                 pressed : false,
27339                 enableToggle : true,
27340                 setValue : function(v) {
27341                     // this trigger toggle.
27342                      
27343                     this.setText(v ? "Hide Caption" : "Show Caption");
27344                     this.setPressed(v != 'block');
27345                 },
27346                 listeners : {
27347                     toggle: function (btn, state)
27348                     {
27349                         var b  = block();
27350                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
27351                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
27352                         b.updateElement();
27353                         syncValue();
27354                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27355                         toolbar.editorcore.onEditorEvent();
27356                     }
27357                 },
27358                 xns : rooui.Toolbar
27359             }
27360         ];
27361         
27362     },
27363     /**
27364      * create a DomHelper friendly object - for use with
27365      * Roo.DomHelper.markup / overwrite / etc..
27366      */
27367     toObject : function()
27368     {
27369         var d = document.createElement('div');
27370         d.innerHTML = this.caption;
27371         
27372         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
27373         
27374         var iw = this.align == 'center' ? this.width : '100%';
27375         var img =   {
27376             tag : 'img',
27377             contenteditable : 'false',
27378             src : this.image_src,
27379             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
27380             style: {
27381                 width : iw,
27382                 maxWidth : iw + ' !important', // this is not getting rendered?
27383                 margin : m  
27384                 
27385             }
27386         };
27387         /*
27388         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
27389                     '<a href="{2}">' + 
27390                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
27391                     '</a>' + 
27392                 '</div>',
27393         */
27394                 
27395         if (this.href.length > 0) {
27396             img = {
27397                 tag : 'a',
27398                 href: this.href,
27399                 contenteditable : 'true',
27400                 cn : [
27401                     img
27402                 ]
27403             };
27404         }
27405         
27406         
27407         if (this.video_url.length > 0) {
27408             img = {
27409                 tag : 'div',
27410                 cls : this.cls,
27411                 frameborder : 0,
27412                 allowfullscreen : true,
27413                 width : 420,  // these are for video tricks - that we replace the outer
27414                 height : 315,
27415                 src : this.video_url,
27416                 cn : [
27417                     img
27418                 ]
27419             };
27420         }
27421         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
27422         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
27423         
27424   
27425         var ret =   {
27426             tag: 'figure',
27427             'data-block' : 'Figure',
27428             'data-width' : this.width, 
27429             contenteditable : 'false',
27430             
27431             style : {
27432                 display: 'block',
27433                 float :  this.align ,
27434                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
27435                 width : this.align == 'center' ? '100%' : this.width,
27436                 margin:  '0px',
27437                 padding: this.align == 'center' ? '0' : '0 10px' ,
27438                 textAlign : this.align   // seems to work for email..
27439                 
27440             },
27441            
27442             
27443             align : this.align,
27444             cn : [
27445                 img,
27446               
27447                 {
27448                     tag: 'figcaption',
27449                     'data-display' : this.caption_display,
27450                     style : {
27451                         textAlign : 'left',
27452                         fontSize : '16px',
27453                         lineHeight : '24px',
27454                         display : this.caption_display,
27455                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
27456                         margin: m,
27457                         width: this.align == 'center' ?  this.width : '100%' 
27458                     
27459                          
27460                     },
27461                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
27462                     cn : [
27463                         {
27464                             tag: 'div',
27465                             style  : {
27466                                 marginTop : '16px',
27467                                 textAlign : 'left'
27468                             },
27469                             align: 'left',
27470                             cn : [
27471                                 {
27472                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
27473                                     tag : 'i',
27474                                     contenteditable : true,
27475                                     html : captionhtml
27476                                 }
27477                                 
27478                             ]
27479                         }
27480                         
27481                     ]
27482                     
27483                 }
27484             ]
27485         };
27486         return ret;
27487          
27488     },
27489     
27490     readElement : function(node)
27491     {
27492         // this should not really come from the link...
27493         this.video_url = this.getVal(node, 'div', 'src');
27494         this.cls = this.getVal(node, 'div', 'class');
27495         this.href = this.getVal(node, 'a', 'href');
27496         
27497         
27498         this.image_src = this.getVal(node, 'img', 'src');
27499          
27500         this.align = this.getVal(node, 'figure', 'align');
27501         var figcaption = this.getVal(node, 'figcaption', false);
27502         if (figcaption !== '') {
27503             this.caption = this.getVal(figcaption, 'i', 'html');
27504         }
27505         
27506
27507         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
27508         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
27509         this.width = this.getVal(node, true, 'data-width');
27510         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
27511         
27512     },
27513     removeNode : function()
27514     {
27515         return this.node;
27516     }
27517     
27518   
27519    
27520      
27521     
27522     
27523     
27524     
27525 })
27526
27527  
27528
27529 /**
27530  * @class Roo.htmleditor.BlockTable
27531  * Block that manages a table
27532  * 
27533  * @constructor
27534  * Create a new Filter.
27535  * @param {Object} config Configuration options
27536  */
27537
27538 Roo.htmleditor.BlockTable = function(cfg)
27539 {
27540     if (cfg.node) {
27541         this.readElement(cfg.node);
27542         this.updateElement(cfg.node);
27543     }
27544     Roo.apply(this, cfg);
27545     if (!cfg.node) {
27546         this.rows = [];
27547         for(var r = 0; r < this.no_row; r++) {
27548             this.rows[r] = [];
27549             for(var c = 0; c < this.no_col; c++) {
27550                 this.rows[r][c] = this.emptyCell();
27551             }
27552         }
27553     }
27554     
27555     
27556 }
27557 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
27558  
27559     rows : false,
27560     no_col : 1,
27561     no_row : 1,
27562     
27563     
27564     width: '100%',
27565     
27566     // used by context menu
27567     friendly_name : 'Table',
27568     deleteTitle : 'Delete Table',
27569     // context menu is drawn once..
27570     
27571     contextMenu : function(toolbar)
27572     {
27573         
27574         var block = function() {
27575             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27576         };
27577         
27578         
27579         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27580         
27581         var syncValue = toolbar.editorcore.syncValue;
27582         
27583         var fields = {};
27584         
27585         return [
27586             {
27587                 xtype : 'TextItem',
27588                 text : "Width: ",
27589                 xns : rooui.Toolbar  //Boostrap?
27590             },
27591             {
27592                 xtype : 'ComboBox',
27593                 allowBlank : false,
27594                 displayField : 'val',
27595                 editable : true,
27596                 listWidth : 100,
27597                 triggerAction : 'all',
27598                 typeAhead : true,
27599                 valueField : 'val',
27600                 width : 100,
27601                 name : 'width',
27602                 listeners : {
27603                     select : function (combo, r, index)
27604                     {
27605                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27606                         var b = block();
27607                         b.width = r.get('val');
27608                         b.updateElement();
27609                         syncValue();
27610                         toolbar.editorcore.onEditorEvent();
27611                     }
27612                 },
27613                 xns : rooui.form,
27614                 store : {
27615                     xtype : 'SimpleStore',
27616                     data : [
27617                         ['100%'],
27618                         ['auto']
27619                     ],
27620                     fields : [ 'val'],
27621                     xns : Roo.data
27622                 }
27623             },
27624             // -------- Cols
27625             
27626             {
27627                 xtype : 'TextItem',
27628                 text : "Columns: ",
27629                 xns : rooui.Toolbar  //Boostrap?
27630             },
27631          
27632             {
27633                 xtype : 'Button',
27634                 text: '-',
27635                 listeners : {
27636                     click : function (_self, e)
27637                     {
27638                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27639                         block().removeColumn();
27640                         syncValue();
27641                         toolbar.editorcore.onEditorEvent();
27642                     }
27643                 },
27644                 xns : rooui.Toolbar
27645             },
27646             {
27647                 xtype : 'Button',
27648                 text: '+',
27649                 listeners : {
27650                     click : function (_self, e)
27651                     {
27652                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27653                         block().addColumn();
27654                         syncValue();
27655                         toolbar.editorcore.onEditorEvent();
27656                     }
27657                 },
27658                 xns : rooui.Toolbar
27659             },
27660             // -------- ROWS
27661             {
27662                 xtype : 'TextItem',
27663                 text : "Rows: ",
27664                 xns : rooui.Toolbar  //Boostrap?
27665             },
27666          
27667             {
27668                 xtype : 'Button',
27669                 text: '-',
27670                 listeners : {
27671                     click : function (_self, e)
27672                     {
27673                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27674                         block().removeRow();
27675                         syncValue();
27676                         toolbar.editorcore.onEditorEvent();
27677                     }
27678                 },
27679                 xns : rooui.Toolbar
27680             },
27681             {
27682                 xtype : 'Button',
27683                 text: '+',
27684                 listeners : {
27685                     click : function (_self, e)
27686                     {
27687                         block().addRow();
27688                         syncValue();
27689                         toolbar.editorcore.onEditorEvent();
27690                     }
27691                 },
27692                 xns : rooui.Toolbar
27693             },
27694             // -------- ROWS
27695             {
27696                 xtype : 'Button',
27697                 text: 'Reset Column Widths',
27698                 listeners : {
27699                     
27700                     click : function (_self, e)
27701                     {
27702                         block().resetWidths();
27703                         syncValue();
27704                         toolbar.editorcore.onEditorEvent();
27705                     }
27706                 },
27707                 xns : rooui.Toolbar
27708             } 
27709             
27710             
27711             
27712         ];
27713         
27714     },
27715     
27716     
27717   /**
27718      * create a DomHelper friendly object - for use with
27719      * Roo.DomHelper.markup / overwrite / etc..
27720      * ?? should it be called with option to hide all editing features?
27721      */
27722     toObject : function()
27723     {
27724         
27725         var ret = {
27726             tag : 'table',
27727             contenteditable : 'false', // this stops cell selection from picking the table.
27728             'data-block' : 'Table',
27729             style : {
27730                 width:  this.width,
27731                 border : 'solid 1px #000', // ??? hard coded?
27732                 'border-collapse' : 'collapse' 
27733             },
27734             cn : [
27735                 { tag : 'tbody' , cn : [] }
27736             ]
27737         };
27738         
27739         // do we have a head = not really 
27740         var ncols = 0;
27741         Roo.each(this.rows, function( row ) {
27742             var tr = {
27743                 tag: 'tr',
27744                 style : {
27745                     margin: '6px',
27746                     border : 'solid 1px #000',
27747                     textAlign : 'left' 
27748                 },
27749                 cn : [ ]
27750             };
27751             
27752             ret.cn[0].cn.push(tr);
27753             // does the row have any properties? ?? height?
27754             var nc = 0;
27755             Roo.each(row, function( cell ) {
27756                 
27757                 var td = {
27758                     tag : 'td',
27759                     contenteditable :  'true',
27760                     'data-block' : 'Td',
27761                     html : cell.html,
27762                     style : cell.style
27763                 };
27764                 if (cell.colspan > 1) {
27765                     td.colspan = cell.colspan ;
27766                     nc += cell.colspan;
27767                 } else {
27768                     nc++;
27769                 }
27770                 if (cell.rowspan > 1) {
27771                     td.rowspan = cell.rowspan ;
27772                 }
27773                 
27774                 
27775                 // widths ?
27776                 tr.cn.push(td);
27777                     
27778                 
27779             }, this);
27780             ncols = Math.max(nc, ncols);
27781             
27782             
27783         }, this);
27784         // add the header row..
27785         
27786         ncols++;
27787          
27788         
27789         return ret;
27790          
27791     },
27792     
27793     readElement : function(node)
27794     {
27795         node  = node ? node : this.node ;
27796         this.width = this.getVal(node, true, 'style', 'width') || '100%';
27797         
27798         this.rows = [];
27799         this.no_row = 0;
27800         var trs = Array.from(node.rows);
27801         trs.forEach(function(tr) {
27802             var row =  [];
27803             this.rows.push(row);
27804             
27805             this.no_row++;
27806             var no_column = 0;
27807             Array.from(tr.cells).forEach(function(td) {
27808                 
27809                 var add = {
27810                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
27811                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
27812                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
27813                     html : td.innerHTML
27814                 };
27815                 no_column += add.colspan;
27816                      
27817                 
27818                 row.push(add);
27819                 
27820                 
27821             },this);
27822             this.no_col = Math.max(this.no_col, no_column);
27823             
27824             
27825         },this);
27826         
27827         
27828     },
27829     normalizeRows: function()
27830     {
27831         var ret= [];
27832         var rid = -1;
27833         this.rows.forEach(function(row) {
27834             rid++;
27835             ret[rid] = [];
27836             row = this.normalizeRow(row);
27837             var cid = 0;
27838             row.forEach(function(c) {
27839                 while (typeof(ret[rid][cid]) != 'undefined') {
27840                     cid++;
27841                 }
27842                 if (typeof(ret[rid]) == 'undefined') {
27843                     ret[rid] = [];
27844                 }
27845                 ret[rid][cid] = c;
27846                 c.row = rid;
27847                 c.col = cid;
27848                 if (c.rowspan < 2) {
27849                     return;
27850                 }
27851                 
27852                 for(var i = 1 ;i < c.rowspan; i++) {
27853                     if (typeof(ret[rid+i]) == 'undefined') {
27854                         ret[rid+i] = [];
27855                     }
27856                     ret[rid+i][cid] = c;
27857                 }
27858             });
27859         }, this);
27860         return ret;
27861     
27862     },
27863     
27864     normalizeRow: function(row)
27865     {
27866         var ret= [];
27867         row.forEach(function(c) {
27868             if (c.colspan < 2) {
27869                 ret.push(c);
27870                 return;
27871             }
27872             for(var i =0 ;i < c.colspan; i++) {
27873                 ret.push(c);
27874             }
27875         });
27876         return ret;
27877     
27878     },
27879     
27880     deleteColumn : function(sel)
27881     {
27882         if (!sel || sel.type != 'col') {
27883             return;
27884         }
27885         if (this.no_col < 2) {
27886             return;
27887         }
27888         
27889         this.rows.forEach(function(row) {
27890             var cols = this.normalizeRow(row);
27891             var col = cols[sel.col];
27892             if (col.colspan > 1) {
27893                 col.colspan --;
27894             } else {
27895                 row.remove(col);
27896             }
27897             
27898         }, this);
27899         this.no_col--;
27900         
27901     },
27902     removeColumn : function()
27903     {
27904         this.deleteColumn({
27905             type: 'col',
27906             col : this.no_col-1
27907         });
27908         this.updateElement();
27909     },
27910     
27911      
27912     addColumn : function()
27913     {
27914         
27915         this.rows.forEach(function(row) {
27916             row.push(this.emptyCell());
27917            
27918         }, this);
27919         this.updateElement();
27920     },
27921     
27922     deleteRow : function(sel)
27923     {
27924         if (!sel || sel.type != 'row') {
27925             return;
27926         }
27927         
27928         if (this.no_row < 2) {
27929             return;
27930         }
27931         
27932         var rows = this.normalizeRows();
27933         
27934         
27935         rows[sel.row].forEach(function(col) {
27936             if (col.rowspan > 1) {
27937                 col.rowspan--;
27938             } else {
27939                 col.remove = 1; // flage it as removed.
27940             }
27941             
27942         }, this);
27943         var newrows = [];
27944         this.rows.forEach(function(row) {
27945             newrow = [];
27946             row.forEach(function(c) {
27947                 if (typeof(c.remove) == 'undefined') {
27948                     newrow.push(c);
27949                 }
27950                 
27951             });
27952             if (newrow.length > 0) {
27953                 newrows.push(row);
27954             }
27955         });
27956         this.rows =  newrows;
27957         
27958         
27959         
27960         this.no_row--;
27961         this.updateElement();
27962         
27963     },
27964     removeRow : function()
27965     {
27966         this.deleteRow({
27967             type: 'row',
27968             row : this.no_row-1
27969         });
27970         
27971     },
27972     
27973      
27974     addRow : function()
27975     {
27976         
27977         var row = [];
27978         for (var i = 0; i < this.no_col; i++ ) {
27979             
27980             row.push(this.emptyCell());
27981            
27982         }
27983         this.rows.push(row);
27984         this.updateElement();
27985         
27986     },
27987      
27988     // the default cell object... at present...
27989     emptyCell : function() {
27990         return (new Roo.htmleditor.BlockTd({})).toObject();
27991         
27992      
27993     },
27994     
27995     removeNode : function()
27996     {
27997         return this.node;
27998     },
27999     
28000     
28001     
28002     resetWidths : function()
28003     {
28004         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
28005             var nn = Roo.htmleditor.Block.factory(n);
28006             nn.width = '';
28007             nn.updateElement(n);
28008         });
28009     }
28010     
28011     
28012     
28013     
28014 })
28015
28016 /**
28017  *
28018  * editing a TD?
28019  *
28020  * since selections really work on the table cell, then editing really should work from there
28021  *
28022  * The original plan was to support merging etc... - but that may not be needed yet..
28023  *
28024  * So this simple version will support:
28025  *   add/remove cols
28026  *   adjust the width +/-
28027  *   reset the width...
28028  *   
28029  *
28030  */
28031
28032
28033  
28034
28035 /**
28036  * @class Roo.htmleditor.BlockTable
28037  * Block that manages a table
28038  * 
28039  * @constructor
28040  * Create a new Filter.
28041  * @param {Object} config Configuration options
28042  */
28043
28044 Roo.htmleditor.BlockTd = function(cfg)
28045 {
28046     if (cfg.node) {
28047         this.readElement(cfg.node);
28048         this.updateElement(cfg.node);
28049     }
28050     Roo.apply(this, cfg);
28051      
28052     
28053     
28054 }
28055 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
28056  
28057     node : false,
28058     
28059     width: '',
28060     textAlign : 'left',
28061     valign : 'top',
28062     
28063     colspan : 1,
28064     rowspan : 1,
28065     
28066     
28067     // used by context menu
28068     friendly_name : 'Table Cell',
28069     deleteTitle : false, // use our customer delete
28070     
28071     // context menu is drawn once..
28072     
28073     contextMenu : function(toolbar)
28074     {
28075         
28076         var cell = function() {
28077             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28078         };
28079         
28080         var table = function() {
28081             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
28082         };
28083         
28084         var lr = false;
28085         var saveSel = function()
28086         {
28087             lr = toolbar.editorcore.getSelection().getRangeAt(0);
28088         }
28089         var restoreSel = function()
28090         {
28091             if (lr) {
28092                 (function() {
28093                     toolbar.editorcore.focus();
28094                     var cr = toolbar.editorcore.getSelection();
28095                     cr.removeAllRanges();
28096                     cr.addRange(lr);
28097                     toolbar.editorcore.onEditorEvent();
28098                 }).defer(10, this);
28099                 
28100                 
28101             }
28102         }
28103         
28104         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28105         
28106         var syncValue = toolbar.editorcore.syncValue;
28107         
28108         var fields = {};
28109         
28110         return [
28111             {
28112                 xtype : 'Button',
28113                 text : 'Edit Table',
28114                 listeners : {
28115                     click : function() {
28116                         var t = toolbar.tb.selectedNode.closest('table');
28117                         toolbar.editorcore.selectNode(t);
28118                         toolbar.editorcore.onEditorEvent();                        
28119                     }
28120                 }
28121                 
28122             },
28123               
28124            
28125              
28126             {
28127                 xtype : 'TextItem',
28128                 text : "Column Width: ",
28129                  xns : rooui.Toolbar 
28130                
28131             },
28132             {
28133                 xtype : 'Button',
28134                 text: '-',
28135                 listeners : {
28136                     click : function (_self, e)
28137                     {
28138                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28139                         cell().shrinkColumn();
28140                         syncValue();
28141                          toolbar.editorcore.onEditorEvent();
28142                     }
28143                 },
28144                 xns : rooui.Toolbar
28145             },
28146             {
28147                 xtype : 'Button',
28148                 text: '+',
28149                 listeners : {
28150                     click : function (_self, e)
28151                     {
28152                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28153                         cell().growColumn();
28154                         syncValue();
28155                         toolbar.editorcore.onEditorEvent();
28156                     }
28157                 },
28158                 xns : rooui.Toolbar
28159             },
28160             
28161             {
28162                 xtype : 'TextItem',
28163                 text : "Vertical Align: ",
28164                 xns : rooui.Toolbar  //Boostrap?
28165             },
28166             {
28167                 xtype : 'ComboBox',
28168                 allowBlank : false,
28169                 displayField : 'val',
28170                 editable : true,
28171                 listWidth : 100,
28172                 triggerAction : 'all',
28173                 typeAhead : true,
28174                 valueField : 'val',
28175                 width : 100,
28176                 name : 'valign',
28177                 listeners : {
28178                     select : function (combo, r, index)
28179                     {
28180                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28181                         var b = cell();
28182                         b.valign = r.get('val');
28183                         b.updateElement();
28184                         syncValue();
28185                         toolbar.editorcore.onEditorEvent();
28186                     }
28187                 },
28188                 xns : rooui.form,
28189                 store : {
28190                     xtype : 'SimpleStore',
28191                     data : [
28192                         ['top'],
28193                         ['middle'],
28194                         ['bottom'] // there are afew more... 
28195                     ],
28196                     fields : [ 'val'],
28197                     xns : Roo.data
28198                 }
28199             },
28200             
28201             {
28202                 xtype : 'TextItem',
28203                 text : "Merge Cells: ",
28204                  xns : rooui.Toolbar 
28205                
28206             },
28207             
28208             
28209             {
28210                 xtype : 'Button',
28211                 text: 'Right',
28212                 listeners : {
28213                     click : function (_self, e)
28214                     {
28215                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28216                         cell().mergeRight();
28217                         //block().growColumn();
28218                         syncValue();
28219                         toolbar.editorcore.onEditorEvent();
28220                     }
28221                 },
28222                 xns : rooui.Toolbar
28223             },
28224              
28225             {
28226                 xtype : 'Button',
28227                 text: 'Below',
28228                 listeners : {
28229                     click : function (_self, e)
28230                     {
28231                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28232                         cell().mergeBelow();
28233                         //block().growColumn();
28234                         syncValue();
28235                         toolbar.editorcore.onEditorEvent();
28236                     }
28237                 },
28238                 xns : rooui.Toolbar
28239             },
28240             {
28241                 xtype : 'TextItem',
28242                 text : "| ",
28243                  xns : rooui.Toolbar 
28244                
28245             },
28246             
28247             {
28248                 xtype : 'Button',
28249                 text: 'Split',
28250                 listeners : {
28251                     click : function (_self, e)
28252                     {
28253                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28254                         cell().split();
28255                         syncValue();
28256                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28257                         toolbar.editorcore.onEditorEvent();
28258                                              
28259                     }
28260                 },
28261                 xns : rooui.Toolbar
28262             },
28263             {
28264                 xtype : 'Fill',
28265                 xns : rooui.Toolbar 
28266                
28267             },
28268         
28269           
28270             {
28271                 xtype : 'Button',
28272                 text: 'Delete',
28273                  
28274                 xns : rooui.Toolbar,
28275                 menu : {
28276                     xtype : 'Menu',
28277                     xns : rooui.menu,
28278                     items : [
28279                         {
28280                             xtype : 'Item',
28281                             html: 'Column',
28282                             listeners : {
28283                                 click : function (_self, e)
28284                                 {
28285                                     var t = table();
28286                                     
28287                                     cell().deleteColumn();
28288                                     syncValue();
28289                                     toolbar.editorcore.selectNode(t.node);
28290                                     toolbar.editorcore.onEditorEvent();   
28291                                 }
28292                             },
28293                             xns : rooui.menu
28294                         },
28295                         {
28296                             xtype : 'Item',
28297                             html: 'Row',
28298                             listeners : {
28299                                 click : function (_self, e)
28300                                 {
28301                                     var t = table();
28302                                     cell().deleteRow();
28303                                     syncValue();
28304                                     
28305                                     toolbar.editorcore.selectNode(t.node);
28306                                     toolbar.editorcore.onEditorEvent();   
28307                                                          
28308                                 }
28309                             },
28310                             xns : rooui.menu
28311                         },
28312                        {
28313                             xtype : 'Separator',
28314                             xns : rooui.menu
28315                         },
28316                         {
28317                             xtype : 'Item',
28318                             html: 'Table',
28319                             listeners : {
28320                                 click : function (_self, e)
28321                                 {
28322                                     var t = table();
28323                                     var nn = t.node.nextSibling || t.node.previousSibling;
28324                                     t.node.parentNode.removeChild(t.node);
28325                                     if (nn) { 
28326                                         toolbar.editorcore.selectNode(nn, true);
28327                                     }
28328                                     toolbar.editorcore.onEditorEvent();   
28329                                                          
28330                                 }
28331                             },
28332                             xns : rooui.menu
28333                         }
28334                     ]
28335                 }
28336             }
28337             
28338             // align... << fixme
28339             
28340         ];
28341         
28342     },
28343     
28344     
28345   /**
28346      * create a DomHelper friendly object - for use with
28347      * Roo.DomHelper.markup / overwrite / etc..
28348      * ?? should it be called with option to hide all editing features?
28349      */
28350  /**
28351      * create a DomHelper friendly object - for use with
28352      * Roo.DomHelper.markup / overwrite / etc..
28353      * ?? should it be called with option to hide all editing features?
28354      */
28355     toObject : function()
28356     {
28357         
28358         var ret = {
28359             tag : 'td',
28360             contenteditable : 'true', // this stops cell selection from picking the table.
28361             'data-block' : 'Td',
28362             valign : this.valign,
28363             style : {  
28364                 'text-align' :  this.textAlign,
28365                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
28366                 'border-collapse' : 'collapse',
28367                 padding : '6px', // 8 for desktop / 4 for mobile
28368                 'vertical-align': this.valign
28369             },
28370             html : this.html
28371         };
28372         if (this.width != '') {
28373             ret.width = this.width;
28374             ret.style.width = this.width;
28375         }
28376         
28377         
28378         if (this.colspan > 1) {
28379             ret.colspan = this.colspan ;
28380         } 
28381         if (this.rowspan > 1) {
28382             ret.rowspan = this.rowspan ;
28383         }
28384         
28385            
28386         
28387         return ret;
28388          
28389     },
28390     
28391     readElement : function(node)
28392     {
28393         node  = node ? node : this.node ;
28394         this.width = node.style.width;
28395         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
28396         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
28397         this.html = node.innerHTML;
28398         
28399         
28400     },
28401      
28402     // the default cell object... at present...
28403     emptyCell : function() {
28404         return {
28405             colspan :  1,
28406             rowspan :  1,
28407             textAlign : 'left',
28408             html : "&nbsp;" // is this going to be editable now?
28409         };
28410      
28411     },
28412     
28413     removeNode : function()
28414     {
28415         return this.node.closest('table');
28416          
28417     },
28418     
28419     cellData : false,
28420     
28421     colWidths : false,
28422     
28423     toTableArray  : function()
28424     {
28425         var ret = [];
28426         var tab = this.node.closest('tr').closest('table');
28427         Array.from(tab.rows).forEach(function(r, ri){
28428             ret[ri] = [];
28429         });
28430         var rn = 0;
28431         this.colWidths = [];
28432         var all_auto = true;
28433         Array.from(tab.rows).forEach(function(r, ri){
28434             
28435             var cn = 0;
28436             Array.from(r.cells).forEach(function(ce, ci){
28437                 var c =  {
28438                     cell : ce,
28439                     row : rn,
28440                     col: cn,
28441                     colspan : ce.colSpan,
28442                     rowspan : ce.rowSpan
28443                 };
28444                 if (ce.isEqualNode(this.node)) {
28445                     this.cellData = c;
28446                 }
28447                 // if we have been filled up by a row?
28448                 if (typeof(ret[rn][cn]) != 'undefined') {
28449                     while(typeof(ret[rn][cn]) != 'undefined') {
28450                         cn++;
28451                     }
28452                     c.col = cn;
28453                 }
28454                 
28455                 if (typeof(this.colWidths[cn]) == 'undefined') {
28456                     this.colWidths[cn] =   ce.style.width;
28457                     if (this.colWidths[cn] != '') {
28458                         all_auto = false;
28459                     }
28460                 }
28461                 
28462                 
28463                 if (c.colspan < 2 && c.rowspan < 2 ) {
28464                     ret[rn][cn] = c;
28465                     cn++;
28466                     return;
28467                 }
28468                 for(var j = 0; j < c.rowspan; j++) {
28469                     if (typeof(ret[rn+j]) == 'undefined') {
28470                         continue; // we have a problem..
28471                     }
28472                     ret[rn+j][cn] = c;
28473                     for(var i = 0; i < c.colspan; i++) {
28474                         ret[rn+j][cn+i] = c;
28475                     }
28476                 }
28477                 
28478                 cn += c.colspan;
28479             }, this);
28480             rn++;
28481         }, this);
28482         
28483         // initalize widths.?
28484         // either all widths or no widths..
28485         if (all_auto) {
28486             this.colWidths[0] = false; // no widths flag.
28487         }
28488         
28489         
28490         return ret;
28491         
28492     },
28493     
28494     
28495     
28496     
28497     mergeRight: function()
28498     {
28499          
28500         // get the contents of the next cell along..
28501         var tr = this.node.closest('tr');
28502         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
28503         if (i >= tr.childNodes.length - 1) {
28504             return; // no cells on right to merge with.
28505         }
28506         var table = this.toTableArray();
28507         
28508         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
28509             return; // nothing right?
28510         }
28511         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
28512         // right cell - must be same rowspan and on the same row.
28513         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
28514             return; // right hand side is not same rowspan.
28515         }
28516         
28517         
28518         
28519         this.node.innerHTML += ' ' + rc.cell.innerHTML;
28520         tr.removeChild(rc.cell);
28521         this.colspan += rc.colspan;
28522         this.node.setAttribute('colspan', this.colspan);
28523
28524     },
28525     
28526     
28527     mergeBelow : function()
28528     {
28529         var table = this.toTableArray();
28530         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
28531             return; // no row below
28532         }
28533         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
28534             return; // nothing right?
28535         }
28536         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
28537         
28538         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
28539             return; // right hand side is not same rowspan.
28540         }
28541         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
28542         rc.cell.parentNode.removeChild(rc.cell);
28543         this.rowspan += rc.rowspan;
28544         this.node.setAttribute('rowspan', this.rowspan);
28545     },
28546     
28547     split: function()
28548     {
28549         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
28550             return;
28551         }
28552         var table = this.toTableArray();
28553         var cd = this.cellData;
28554         this.rowspan = 1;
28555         this.colspan = 1;
28556         
28557         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
28558             
28559             
28560             
28561             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
28562                 if (r == cd.row && c == cd.col) {
28563                     this.node.removeAttribute('rowspan');
28564                     this.node.removeAttribute('colspan');
28565                     continue;
28566                 }
28567                  
28568                 var ntd = this.node.cloneNode(); // which col/row should be 0..
28569                 ntd.removeAttribute('id'); //
28570                 //ntd.style.width  = '';
28571                 ntd.innerHTML = '';
28572                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
28573             }
28574             
28575         }
28576         this.redrawAllCells(table);
28577         
28578          
28579         
28580     },
28581     
28582     
28583     
28584     redrawAllCells: function(table)
28585     {
28586         
28587          
28588         var tab = this.node.closest('tr').closest('table');
28589         var ctr = tab.rows[0].parentNode;
28590         Array.from(tab.rows).forEach(function(r, ri){
28591             
28592             Array.from(r.cells).forEach(function(ce, ci){
28593                 ce.parentNode.removeChild(ce);
28594             });
28595             r.parentNode.removeChild(r);
28596         });
28597         for(var r = 0 ; r < table.length; r++) {
28598             var re = tab.rows[r];
28599             
28600             var re = tab.ownerDocument.createElement('tr');
28601             ctr.appendChild(re);
28602             for(var c = 0 ; c < table[r].length; c++) {
28603                 if (table[r][c].cell === false) {
28604                     continue;
28605                 }
28606                 
28607                 re.appendChild(table[r][c].cell);
28608                  
28609                 table[r][c].cell = false;
28610             }
28611         }
28612         
28613     },
28614     updateWidths : function(table)
28615     {
28616         for(var r = 0 ; r < table.length; r++) {
28617            
28618             for(var c = 0 ; c < table[r].length; c++) {
28619                 if (table[r][c].cell === false) {
28620                     continue;
28621                 }
28622                 
28623                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
28624                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
28625                     el.width = Math.floor(this.colWidths[c])  +'%';
28626                     el.updateElement(el.node);
28627                 }
28628                 table[r][c].cell = false; // done
28629             }
28630         }
28631     },
28632     normalizeWidths : function(table)
28633     {
28634     
28635         if (this.colWidths[0] === false) {
28636             var nw = 100.0 / this.colWidths.length;
28637             this.colWidths.forEach(function(w,i) {
28638                 this.colWidths[i] = nw;
28639             },this);
28640             return;
28641         }
28642     
28643         var t = 0, missing = [];
28644         
28645         this.colWidths.forEach(function(w,i) {
28646             //if you mix % and
28647             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
28648             var add =  this.colWidths[i];
28649             if (add > 0) {
28650                 t+=add;
28651                 return;
28652             }
28653             missing.push(i);
28654             
28655             
28656         },this);
28657         var nc = this.colWidths.length;
28658         if (missing.length) {
28659             var mult = (nc - missing.length) / (1.0 * nc);
28660             var t = mult * t;
28661             var ew = (100 -t) / (1.0 * missing.length);
28662             this.colWidths.forEach(function(w,i) {
28663                 if (w > 0) {
28664                     this.colWidths[i] = w * mult;
28665                     return;
28666                 }
28667                 
28668                 this.colWidths[i] = ew;
28669             }, this);
28670             // have to make up numbers..
28671              
28672         }
28673         // now we should have all the widths..
28674         
28675     
28676     },
28677     
28678     shrinkColumn : function()
28679     {
28680         var table = this.toTableArray();
28681         this.normalizeWidths(table);
28682         var col = this.cellData.col;
28683         var nw = this.colWidths[col] * 0.8;
28684         if (nw < 5) {
28685             return;
28686         }
28687         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
28688         this.colWidths.forEach(function(w,i) {
28689             if (i == col) {
28690                  this.colWidths[i] = nw;
28691                 return;
28692             }
28693             this.colWidths[i] += otherAdd
28694         }, this);
28695         this.updateWidths(table);
28696          
28697     },
28698     growColumn : function()
28699     {
28700         var table = this.toTableArray();
28701         this.normalizeWidths(table);
28702         var col = this.cellData.col;
28703         var nw = this.colWidths[col] * 1.2;
28704         if (nw > 90) {
28705             return;
28706         }
28707         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
28708         this.colWidths.forEach(function(w,i) {
28709             if (i == col) {
28710                 this.colWidths[i] = nw;
28711                 return;
28712             }
28713             this.colWidths[i] -= otherSub
28714         }, this);
28715         this.updateWidths(table);
28716          
28717     },
28718     deleteRow : function()
28719     {
28720         // delete this rows 'tr'
28721         // if any of the cells in this row have a rowspan > 1 && row!= this row..
28722         // then reduce the rowspan.
28723         var table = this.toTableArray();
28724         // this.cellData.row;
28725         for (var i =0;i< table[this.cellData.row].length ; i++) {
28726             var c = table[this.cellData.row][i];
28727             if (c.row != this.cellData.row) {
28728                 
28729                 c.rowspan--;
28730                 c.cell.setAttribute('rowspan', c.rowspan);
28731                 continue;
28732             }
28733             if (c.rowspan > 1) {
28734                 c.rowspan--;
28735                 c.cell.setAttribute('rowspan', c.rowspan);
28736             }
28737         }
28738         table.splice(this.cellData.row,1);
28739         this.redrawAllCells(table);
28740         
28741     },
28742     deleteColumn : function()
28743     {
28744         var table = this.toTableArray();
28745         
28746         for (var i =0;i< table.length ; i++) {
28747             var c = table[i][this.cellData.col];
28748             if (c.col != this.cellData.col) {
28749                 table[i][this.cellData.col].colspan--;
28750             } else if (c.colspan > 1) {
28751                 c.colspan--;
28752                 c.cell.setAttribute('colspan', c.colspan);
28753             }
28754             table[i].splice(this.cellData.col,1);
28755         }
28756         
28757         this.redrawAllCells(table);
28758     }
28759     
28760     
28761     
28762     
28763 })
28764
28765 //<script type="text/javascript">
28766
28767 /*
28768  * Based  Ext JS Library 1.1.1
28769  * Copyright(c) 2006-2007, Ext JS, LLC.
28770  * LGPL
28771  *
28772  */
28773  
28774 /**
28775  * @class Roo.HtmlEditorCore
28776  * @extends Roo.Component
28777  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
28778  *
28779  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
28780  */
28781
28782 Roo.HtmlEditorCore = function(config){
28783     
28784     
28785     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
28786     
28787     
28788     this.addEvents({
28789         /**
28790          * @event initialize
28791          * Fires when the editor is fully initialized (including the iframe)
28792          * @param {Roo.HtmlEditorCore} this
28793          */
28794         initialize: true,
28795         /**
28796          * @event activate
28797          * Fires when the editor is first receives the focus. Any insertion must wait
28798          * until after this event.
28799          * @param {Roo.HtmlEditorCore} this
28800          */
28801         activate: true,
28802          /**
28803          * @event beforesync
28804          * Fires before the textarea is updated with content from the editor iframe. Return false
28805          * to cancel the sync.
28806          * @param {Roo.HtmlEditorCore} this
28807          * @param {String} html
28808          */
28809         beforesync: true,
28810          /**
28811          * @event beforepush
28812          * Fires before the iframe editor is updated with content from the textarea. Return false
28813          * to cancel the push.
28814          * @param {Roo.HtmlEditorCore} this
28815          * @param {String} html
28816          */
28817         beforepush: true,
28818          /**
28819          * @event sync
28820          * Fires when the textarea is updated with content from the editor iframe.
28821          * @param {Roo.HtmlEditorCore} this
28822          * @param {String} html
28823          */
28824         sync: true,
28825          /**
28826          * @event push
28827          * Fires when the iframe editor is updated with content from the textarea.
28828          * @param {Roo.HtmlEditorCore} this
28829          * @param {String} html
28830          */
28831         push: true,
28832         
28833         /**
28834          * @event editorevent
28835          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
28836          * @param {Roo.HtmlEditorCore} this
28837          */
28838         editorevent: true 
28839          
28840         
28841     });
28842     
28843     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
28844     
28845     // defaults : white / black...
28846     this.applyBlacklists();
28847     
28848     
28849     
28850 };
28851
28852
28853 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
28854
28855
28856      /**
28857      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
28858      */
28859     
28860     owner : false,
28861     
28862      /**
28863      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
28864      *                        Roo.resizable.
28865      */
28866     resizable : false,
28867      /**
28868      * @cfg {Number} height (in pixels)
28869      */   
28870     height: 300,
28871    /**
28872      * @cfg {Number} width (in pixels)
28873      */   
28874     width: 500,
28875      /**
28876      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
28877      *         if you are doing an email editor, this probably needs disabling, it's designed
28878      */
28879     autoClean: true,
28880     
28881     /**
28882      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
28883      */
28884     enableBlocks : true,
28885     /**
28886      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
28887      * 
28888      */
28889     stylesheets: false,
28890      /**
28891      * @cfg {String} language default en - language of text (usefull for rtl languages)
28892      * 
28893      */
28894     language: 'en',
28895     
28896     /**
28897      * @cfg {boolean} allowComments - default false - allow comments in HTML source
28898      *          - by default they are stripped - if you are editing email you may need this.
28899      */
28900     allowComments: false,
28901     // id of frame..
28902     frameId: false,
28903     
28904     // private properties
28905     validationEvent : false,
28906     deferHeight: true,
28907     initialized : false,
28908     activated : false,
28909     sourceEditMode : false,
28910     onFocus : Roo.emptyFn,
28911     iframePad:3,
28912     hideMode:'offsets',
28913     
28914     clearUp: true,
28915     
28916     // blacklist + whitelisted elements..
28917     black: false,
28918     white: false,
28919      
28920     bodyCls : '',
28921
28922     
28923     undoManager : false,
28924     /**
28925      * Protected method that will not generally be called directly. It
28926      * is called when the editor initializes the iframe with HTML contents. Override this method if you
28927      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
28928      */
28929     getDocMarkup : function(){
28930         // body styles..
28931         var st = '';
28932         
28933         // inherit styels from page...?? 
28934         if (this.stylesheets === false) {
28935             
28936             Roo.get(document.head).select('style').each(function(node) {
28937                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
28938             });
28939             
28940             Roo.get(document.head).select('link').each(function(node) { 
28941                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
28942             });
28943             
28944         } else if (!this.stylesheets.length) {
28945                 // simple..
28946                 st = '<style type="text/css">' +
28947                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
28948                    '</style>';
28949         } else {
28950             for (var i in this.stylesheets) {
28951                 if (typeof(this.stylesheets[i]) != 'string') {
28952                     continue;
28953                 }
28954                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
28955             }
28956             
28957         }
28958         
28959         st +=  '<style type="text/css">' +
28960             'IMG { cursor: pointer } ' +
28961         '</style>';
28962         
28963         st += '<meta name="google" content="notranslate">';
28964         
28965         var cls = 'notranslate roo-htmleditor-body';
28966         
28967         if(this.bodyCls.length){
28968             cls += ' ' + this.bodyCls;
28969         }
28970         
28971         return '<html  class="notranslate" translate="no"><head>' + st  +
28972             //<style type="text/css">' +
28973             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
28974             //'</style>' +
28975             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
28976     },
28977
28978     // private
28979     onRender : function(ct, position)
28980     {
28981         var _t = this;
28982         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
28983         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
28984         
28985         
28986         this.el.dom.style.border = '0 none';
28987         this.el.dom.setAttribute('tabIndex', -1);
28988         this.el.addClass('x-hidden hide');
28989         
28990         
28991         
28992         if(Roo.isIE){ // fix IE 1px bogus margin
28993             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
28994         }
28995        
28996         
28997         this.frameId = Roo.id();
28998         
28999          
29000         
29001         var iframe = this.owner.wrap.createChild({
29002             tag: 'iframe',
29003             cls: 'form-control', // bootstrap..
29004             id: this.frameId,
29005             name: this.frameId,
29006             frameBorder : 'no',
29007             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
29008         }, this.el
29009         );
29010         
29011         
29012         this.iframe = iframe.dom;
29013
29014         this.assignDocWin();
29015         
29016         this.doc.designMode = 'on';
29017        
29018         this.doc.open();
29019         this.doc.write(this.getDocMarkup());
29020         this.doc.close();
29021
29022         
29023         var task = { // must defer to wait for browser to be ready
29024             run : function(){
29025                 //console.log("run task?" + this.doc.readyState);
29026                 this.assignDocWin();
29027                 if(this.doc.body || this.doc.readyState == 'complete'){
29028                     try {
29029                         this.doc.designMode="on";
29030                         
29031                     } catch (e) {
29032                         return;
29033                     }
29034                     Roo.TaskMgr.stop(task);
29035                     this.initEditor.defer(10, this);
29036                 }
29037             },
29038             interval : 10,
29039             duration: 10000,
29040             scope: this
29041         };
29042         Roo.TaskMgr.start(task);
29043
29044     },
29045
29046     // private
29047     onResize : function(w, h)
29048     {
29049          Roo.log('resize: ' +w + ',' + h );
29050         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
29051         if(!this.iframe){
29052             return;
29053         }
29054         if(typeof w == 'number'){
29055             
29056             this.iframe.style.width = w + 'px';
29057         }
29058         if(typeof h == 'number'){
29059             
29060             this.iframe.style.height = h + 'px';
29061             if(this.doc){
29062                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
29063             }
29064         }
29065         
29066     },
29067
29068     /**
29069      * Toggles the editor between standard and source edit mode.
29070      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
29071      */
29072     toggleSourceEdit : function(sourceEditMode){
29073         
29074         this.sourceEditMode = sourceEditMode === true;
29075         
29076         if(this.sourceEditMode){
29077  
29078             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
29079             
29080         }else{
29081             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
29082             //this.iframe.className = '';
29083             this.deferFocus();
29084         }
29085         //this.setSize(this.owner.wrap.getSize());
29086         //this.fireEvent('editmodechange', this, this.sourceEditMode);
29087     },
29088
29089     
29090   
29091
29092     /**
29093      * Protected method that will not generally be called directly. If you need/want
29094      * custom HTML cleanup, this is the method you should override.
29095      * @param {String} html The HTML to be cleaned
29096      * return {String} The cleaned HTML
29097      */
29098     cleanHtml : function(html)
29099     {
29100         html = String(html);
29101         if(html.length > 5){
29102             if(Roo.isSafari){ // strip safari nonsense
29103                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
29104             }
29105         }
29106         if(html == '&nbsp;'){
29107             html = '';
29108         }
29109         return html;
29110     },
29111
29112     /**
29113      * HTML Editor -> Textarea
29114      * Protected method that will not generally be called directly. Syncs the contents
29115      * of the editor iframe with the textarea.
29116      */
29117     syncValue : function()
29118     {
29119         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
29120         if(this.initialized){
29121             
29122             if (this.undoManager) {
29123                 this.undoManager.addEvent();
29124             }
29125
29126             
29127             var bd = (this.doc.body || this.doc.documentElement);
29128            
29129             
29130             var sel = this.win.getSelection();
29131             
29132             var div = document.createElement('div');
29133             div.innerHTML = bd.innerHTML;
29134             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
29135             if (gtx.length > 0) {
29136                 var rm = gtx.item(0).parentNode;
29137                 rm.parentNode.removeChild(rm);
29138             }
29139             
29140            
29141             if (this.enableBlocks) {
29142                 new Roo.htmleditor.FilterBlock({ node : div });
29143             }
29144             //?? tidy?
29145             var tidy = new Roo.htmleditor.TidySerializer({
29146                 inner:  true
29147             });
29148             var html  = tidy.serialize(div);
29149             
29150             
29151             if(Roo.isSafari){
29152                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
29153                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
29154                 if(m && m[1]){
29155                     html = '<div style="'+m[0]+'">' + html + '</div>';
29156                 }
29157             }
29158             html = this.cleanHtml(html);
29159             // fix up the special chars.. normaly like back quotes in word...
29160             // however we do not want to do this with chinese..
29161             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
29162                 
29163                 var cc = match.charCodeAt();
29164
29165                 // Get the character value, handling surrogate pairs
29166                 if (match.length == 2) {
29167                     // It's a surrogate pair, calculate the Unicode code point
29168                     var high = match.charCodeAt(0) - 0xD800;
29169                     var low  = match.charCodeAt(1) - 0xDC00;
29170                     cc = (high * 0x400) + low + 0x10000;
29171                 }  else if (
29172                     (cc >= 0x4E00 && cc < 0xA000 ) ||
29173                     (cc >= 0x3400 && cc < 0x4E00 ) ||
29174                     (cc >= 0xf900 && cc < 0xfb00 )
29175                 ) {
29176                         return match;
29177                 }  
29178          
29179                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
29180                 return "&#" + cc + ";";
29181                 
29182                 
29183             });
29184             
29185             
29186              
29187             if(this.owner.fireEvent('beforesync', this, html) !== false){
29188                 this.el.dom.value = html;
29189                 this.owner.fireEvent('sync', this, html);
29190             }
29191         }
29192     },
29193
29194     /**
29195      * TEXTAREA -> EDITABLE
29196      * Protected method that will not generally be called directly. Pushes the value of the textarea
29197      * into the iframe editor.
29198      */
29199     pushValue : function()
29200     {
29201         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
29202         if(this.initialized){
29203             var v = this.el.dom.value.trim();
29204             
29205             
29206             if(this.owner.fireEvent('beforepush', this, v) !== false){
29207                 var d = (this.doc.body || this.doc.documentElement);
29208                 d.innerHTML = v;
29209                  
29210                 this.el.dom.value = d.innerHTML;
29211                 this.owner.fireEvent('push', this, v);
29212             }
29213             if (this.autoClean) {
29214                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
29215                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
29216             }
29217             if (this.enableBlocks) {
29218                 Roo.htmleditor.Block.initAll(this.doc.body);
29219             }
29220             
29221             this.updateLanguage();
29222             
29223             var lc = this.doc.body.lastChild;
29224             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
29225                 // add an extra line at the end.
29226                 this.doc.body.appendChild(this.doc.createElement('br'));
29227             }
29228             
29229             
29230         }
29231     },
29232
29233     // private
29234     deferFocus : function(){
29235         this.focus.defer(10, this);
29236     },
29237
29238     // doc'ed in Field
29239     focus : function(){
29240         if(this.win && !this.sourceEditMode){
29241             this.win.focus();
29242         }else{
29243             this.el.focus();
29244         }
29245     },
29246     
29247     assignDocWin: function()
29248     {
29249         var iframe = this.iframe;
29250         
29251          if(Roo.isIE){
29252             this.doc = iframe.contentWindow.document;
29253             this.win = iframe.contentWindow;
29254         } else {
29255 //            if (!Roo.get(this.frameId)) {
29256 //                return;
29257 //            }
29258 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29259 //            this.win = Roo.get(this.frameId).dom.contentWindow;
29260             
29261             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
29262                 return;
29263             }
29264             
29265             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29266             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
29267         }
29268     },
29269     
29270     // private
29271     initEditor : function(){
29272         //console.log("INIT EDITOR");
29273         this.assignDocWin();
29274         
29275         
29276         
29277         this.doc.designMode="on";
29278         this.doc.open();
29279         this.doc.write(this.getDocMarkup());
29280         this.doc.close();
29281         
29282         var dbody = (this.doc.body || this.doc.documentElement);
29283         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
29284         // this copies styles from the containing element into thsi one..
29285         // not sure why we need all of this..
29286         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
29287         
29288         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
29289         //ss['background-attachment'] = 'fixed'; // w3c
29290         dbody.bgProperties = 'fixed'; // ie
29291         dbody.setAttribute("translate", "no");
29292         
29293         //Roo.DomHelper.applyStyles(dbody, ss);
29294         Roo.EventManager.on(this.doc, {
29295              
29296             'mouseup': this.onEditorEvent,
29297             'dblclick': this.onEditorEvent,
29298             'click': this.onEditorEvent,
29299             'keyup': this.onEditorEvent,
29300             
29301             buffer:100,
29302             scope: this
29303         });
29304         Roo.EventManager.on(this.doc, {
29305             'paste': this.onPasteEvent,
29306             scope : this
29307         });
29308         if(Roo.isGecko){
29309             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
29310         }
29311         //??? needed???
29312         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
29313             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
29314         }
29315         this.initialized = true;
29316
29317         
29318         // initialize special key events - enter
29319         new Roo.htmleditor.KeyEnter({core : this});
29320         
29321          
29322         
29323         this.owner.fireEvent('initialize', this);
29324         this.pushValue();
29325     },
29326     // this is to prevent a href clicks resulting in a redirect?
29327    
29328     onPasteEvent : function(e,v)
29329     {
29330         // I think we better assume paste is going to be a dirty load of rubish from word..
29331         
29332         // even pasting into a 'email version' of this widget will have to clean up that mess.
29333         var cd = (e.browserEvent.clipboardData || window.clipboardData);
29334         
29335         // check what type of paste - if it's an image, then handle it differently.
29336         if (cd.files && cd.files.length > 0) {
29337             // pasting images?
29338             var urlAPI = (window.createObjectURL && window) || 
29339                 (window.URL && URL.revokeObjectURL && URL) || 
29340                 (window.webkitURL && webkitURL);
29341     
29342             var url = urlAPI.createObjectURL( cd.files[0]);
29343             this.insertAtCursor('<img src=" + url + ">');
29344             return false;
29345         }
29346         if (cd.types.indexOf('text/html') < 0 ) {
29347             return false;
29348         }
29349         var images = [];
29350         var html = cd.getData('text/html'); // clipboard event
29351         if (cd.types.indexOf('text/rtf') > -1) {
29352             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
29353             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
29354         }
29355         //Roo.log(images);
29356         //Roo.log(imgs);
29357         // fixme..
29358         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
29359                        .map(function(g) { return g.toDataURL(); })
29360                        .filter(function(g) { return g != 'about:blank'; });
29361         
29362         
29363         html = this.cleanWordChars(html);
29364         
29365         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
29366         
29367         
29368         var sn = this.getParentElement();
29369         // check if d contains a table, and prevent nesting??
29370         //Roo.log(d.getElementsByTagName('table'));
29371         //Roo.log(sn);
29372         //Roo.log(sn.closest('table'));
29373         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
29374             e.preventDefault();
29375             this.insertAtCursor("You can not nest tables");
29376             //Roo.log("prevent?"); // fixme - 
29377             return false;
29378         }
29379         
29380         if (images.length > 0) {
29381             Roo.each(d.getElementsByTagName('img'), function(img, i) {
29382                 img.setAttribute('src', images[i]);
29383             });
29384         }
29385         if (this.autoClean) {
29386             new Roo.htmleditor.FilterWord({ node : d });
29387             
29388             new Roo.htmleditor.FilterStyleToTag({ node : d });
29389             new Roo.htmleditor.FilterAttributes({
29390                 node : d,
29391                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width'],
29392                 attrib_clean : ['href', 'src' ] 
29393             });
29394             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
29395             // should be fonts..
29396             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
29397             new Roo.htmleditor.FilterParagraph({ node : d });
29398             new Roo.htmleditor.FilterSpan({ node : d });
29399             new Roo.htmleditor.FilterLongBr({ node : d });
29400             new Roo.htmleditor.FilterComment({ node : d });
29401             
29402             
29403         }
29404         if (this.enableBlocks) {
29405                 
29406             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
29407                 if (img.closest('figure')) { // assume!! that it's aready
29408                     return;
29409                 }
29410                 var fig  = new Roo.htmleditor.BlockFigure({
29411                     image_src  : img.src
29412                 });
29413                 fig.updateElement(img); // replace it..
29414                 
29415             });
29416         }
29417         
29418         
29419         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
29420         if (this.enableBlocks) {
29421             Roo.htmleditor.Block.initAll(this.doc.body);
29422         }
29423          
29424         
29425         e.preventDefault();
29426         return false;
29427         // default behaveiour should be our local cleanup paste? (optional?)
29428         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
29429         //this.owner.fireEvent('paste', e, v);
29430     },
29431     // private
29432     onDestroy : function(){
29433         
29434         
29435         
29436         if(this.rendered){
29437             
29438             //for (var i =0; i < this.toolbars.length;i++) {
29439             //    // fixme - ask toolbars for heights?
29440             //    this.toolbars[i].onDestroy();
29441            // }
29442             
29443             //this.wrap.dom.innerHTML = '';
29444             //this.wrap.remove();
29445         }
29446     },
29447
29448     // private
29449     onFirstFocus : function(){
29450         
29451         this.assignDocWin();
29452         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
29453         
29454         this.activated = true;
29455          
29456     
29457         if(Roo.isGecko){ // prevent silly gecko errors
29458             this.win.focus();
29459             var s = this.win.getSelection();
29460             if(!s.focusNode || s.focusNode.nodeType != 3){
29461                 var r = s.getRangeAt(0);
29462                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
29463                 r.collapse(true);
29464                 this.deferFocus();
29465             }
29466             try{
29467                 this.execCmd('useCSS', true);
29468                 this.execCmd('styleWithCSS', false);
29469             }catch(e){}
29470         }
29471         this.owner.fireEvent('activate', this);
29472     },
29473
29474     // private
29475     adjustFont: function(btn){
29476         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
29477         //if(Roo.isSafari){ // safari
29478         //    adjust *= 2;
29479        // }
29480         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
29481         if(Roo.isSafari){ // safari
29482             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
29483             v =  (v < 10) ? 10 : v;
29484             v =  (v > 48) ? 48 : v;
29485             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
29486             
29487         }
29488         
29489         
29490         v = Math.max(1, v+adjust);
29491         
29492         this.execCmd('FontSize', v  );
29493     },
29494
29495     onEditorEvent : function(e)
29496     {
29497          
29498         
29499         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
29500             return; // we do not handle this.. (undo manager does..)
29501         }
29502         // in theory this detects if the last element is not a br, then we try and do that.
29503         // its so clicking in space at bottom triggers adding a br and moving the cursor.
29504         if (e &&
29505             e.target.nodeName == 'BODY' &&
29506             e.type == "mouseup" &&
29507             this.doc.body.lastChild
29508            ) {
29509             var lc = this.doc.body.lastChild;
29510             // gtx-trans is google translate plugin adding crap.
29511             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
29512                 lc = lc.previousSibling;
29513             }
29514             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
29515             // if last element is <BR> - then dont do anything.
29516             
29517                 var ns = this.doc.createElement('br');
29518                 this.doc.body.appendChild(ns);
29519                 range = this.doc.createRange();
29520                 range.setStartAfter(ns);
29521                 range.collapse(true);
29522                 var sel = this.win.getSelection();
29523                 sel.removeAllRanges();
29524                 sel.addRange(range);
29525             }
29526         }
29527         
29528         
29529         
29530         this.fireEditorEvent(e);
29531       //  this.updateToolbar();
29532         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
29533     },
29534     
29535     fireEditorEvent: function(e)
29536     {
29537         this.owner.fireEvent('editorevent', this, e);
29538     },
29539
29540     insertTag : function(tg)
29541     {
29542         // could be a bit smarter... -> wrap the current selected tRoo..
29543         if (tg.toLowerCase() == 'span' ||
29544             tg.toLowerCase() == 'code' ||
29545             tg.toLowerCase() == 'sup' ||
29546             tg.toLowerCase() == 'sub' 
29547             ) {
29548             
29549             range = this.createRange(this.getSelection());
29550             var wrappingNode = this.doc.createElement(tg.toLowerCase());
29551             wrappingNode.appendChild(range.extractContents());
29552             range.insertNode(wrappingNode);
29553
29554             return;
29555             
29556             
29557             
29558         }
29559         this.execCmd("formatblock",   tg);
29560         this.undoManager.addEvent(); 
29561     },
29562     
29563     insertText : function(txt)
29564     {
29565         
29566         
29567         var range = this.createRange();
29568         range.deleteContents();
29569                //alert(Sender.getAttribute('label'));
29570                
29571         range.insertNode(this.doc.createTextNode(txt));
29572         this.undoManager.addEvent();
29573     } ,
29574     
29575      
29576
29577     /**
29578      * Executes a Midas editor command on the editor document and performs necessary focus and
29579      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
29580      * @param {String} cmd The Midas command
29581      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
29582      */
29583     relayCmd : function(cmd, value)
29584     {
29585         
29586         switch (cmd) {
29587             case 'justifyleft':
29588             case 'justifyright':
29589             case 'justifycenter':
29590                 // if we are in a cell, then we will adjust the
29591                 var n = this.getParentElement();
29592                 var td = n.closest('td');
29593                 if (td) {
29594                     var bl = Roo.htmleditor.Block.factory(td);
29595                     bl.textAlign = cmd.replace('justify','');
29596                     bl.updateElement();
29597                     this.owner.fireEvent('editorevent', this);
29598                     return;
29599                 }
29600                 this.execCmd('styleWithCSS', true); // 
29601                 break;
29602             case 'bold':
29603             case 'italic':
29604                 // if there is no selection, then we insert, and set the curson inside it..
29605                 this.execCmd('styleWithCSS', false); 
29606                 break;
29607                 
29608         
29609             default:
29610                 break;
29611         }
29612         
29613         
29614         this.win.focus();
29615         this.execCmd(cmd, value);
29616         this.owner.fireEvent('editorevent', this);
29617         //this.updateToolbar();
29618         this.owner.deferFocus();
29619     },
29620
29621     /**
29622      * Executes a Midas editor command directly on the editor document.
29623      * For visual commands, you should use {@link #relayCmd} instead.
29624      * <b>This should only be called after the editor is initialized.</b>
29625      * @param {String} cmd The Midas command
29626      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
29627      */
29628     execCmd : function(cmd, value){
29629         this.doc.execCommand(cmd, false, value === undefined ? null : value);
29630         this.syncValue();
29631     },
29632  
29633  
29634    
29635     /**
29636      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
29637      * to insert tRoo.
29638      * @param {String} text | dom node.. 
29639      */
29640     insertAtCursor : function(text)
29641     {
29642         
29643         if(!this.activated){
29644             return;
29645         }
29646          
29647         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
29648             this.win.focus();
29649             
29650             
29651             // from jquery ui (MIT licenced)
29652             var range, node;
29653             var win = this.win;
29654             
29655             if (win.getSelection && win.getSelection().getRangeAt) {
29656                 
29657                 // delete the existing?
29658                 
29659                 this.createRange(this.getSelection()).deleteContents();
29660                 range = win.getSelection().getRangeAt(0);
29661                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
29662                 range.insertNode(node);
29663                 range = range.cloneRange();
29664                 range.collapse(false);
29665                  
29666                 win.getSelection().removeAllRanges();
29667                 win.getSelection().addRange(range);
29668                 
29669                 
29670                 
29671             } else if (win.document.selection && win.document.selection.createRange) {
29672                 // no firefox support
29673                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
29674                 win.document.selection.createRange().pasteHTML(txt);
29675             
29676             } else {
29677                 // no firefox support
29678                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
29679                 this.execCmd('InsertHTML', txt);
29680             } 
29681             this.syncValue();
29682             
29683             this.deferFocus();
29684         }
29685     },
29686  // private
29687     mozKeyPress : function(e){
29688         if(e.ctrlKey){
29689             var c = e.getCharCode(), cmd;
29690           
29691             if(c > 0){
29692                 c = String.fromCharCode(c).toLowerCase();
29693                 switch(c){
29694                     case 'b':
29695                         cmd = 'bold';
29696                         break;
29697                     case 'i':
29698                         cmd = 'italic';
29699                         break;
29700                     
29701                     case 'u':
29702                         cmd = 'underline';
29703                         break;
29704                     
29705                     //case 'v':
29706                       //  this.cleanUpPaste.defer(100, this);
29707                       //  return;
29708                         
29709                 }
29710                 if(cmd){
29711                     
29712                     this.relayCmd(cmd);
29713                     //this.win.focus();
29714                     //this.execCmd(cmd);
29715                     //this.deferFocus();
29716                     e.preventDefault();
29717                 }
29718                 
29719             }
29720         }
29721     },
29722
29723     // private
29724     fixKeys : function(){ // load time branching for fastest keydown performance
29725         
29726         
29727         if(Roo.isIE){
29728             return function(e){
29729                 var k = e.getKey(), r;
29730                 if(k == e.TAB){
29731                     e.stopEvent();
29732                     r = this.doc.selection.createRange();
29733                     if(r){
29734                         r.collapse(true);
29735                         r.pasteHTML('&#160;&#160;&#160;&#160;');
29736                         this.deferFocus();
29737                     }
29738                     return;
29739                 }
29740                 /// this is handled by Roo.htmleditor.KeyEnter
29741                  /*
29742                 if(k == e.ENTER){
29743                     r = this.doc.selection.createRange();
29744                     if(r){
29745                         var target = r.parentElement();
29746                         if(!target || target.tagName.toLowerCase() != 'li'){
29747                             e.stopEvent();
29748                             r.pasteHTML('<br/>');
29749                             r.collapse(false);
29750                             r.select();
29751                         }
29752                     }
29753                 }
29754                 */
29755                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29756                 //    this.cleanUpPaste.defer(100, this);
29757                 //    return;
29758                 //}
29759                 
29760                 
29761             };
29762         }else if(Roo.isOpera){
29763             return function(e){
29764                 var k = e.getKey();
29765                 if(k == e.TAB){
29766                     e.stopEvent();
29767                     this.win.focus();
29768                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
29769                     this.deferFocus();
29770                 }
29771                
29772                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29773                 //    this.cleanUpPaste.defer(100, this);
29774                  //   return;
29775                 //}
29776                 
29777             };
29778         }else if(Roo.isSafari){
29779             return function(e){
29780                 var k = e.getKey();
29781                 
29782                 if(k == e.TAB){
29783                     e.stopEvent();
29784                     this.execCmd('InsertText','\t');
29785                     this.deferFocus();
29786                     return;
29787                 }
29788                  this.mozKeyPress(e);
29789                 
29790                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29791                  //   this.cleanUpPaste.defer(100, this);
29792                  //   return;
29793                // }
29794                 
29795              };
29796         }
29797     }(),
29798     
29799     getAllAncestors: function()
29800     {
29801         var p = this.getSelectedNode();
29802         var a = [];
29803         if (!p) {
29804             a.push(p); // push blank onto stack..
29805             p = this.getParentElement();
29806         }
29807         
29808         
29809         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
29810             a.push(p);
29811             p = p.parentNode;
29812         }
29813         a.push(this.doc.body);
29814         return a;
29815     },
29816     lastSel : false,
29817     lastSelNode : false,
29818     
29819     
29820     getSelection : function() 
29821     {
29822         this.assignDocWin();
29823         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
29824     },
29825     /**
29826      * Select a dom node
29827      * @param {DomElement} node the node to select
29828      */
29829     selectNode : function(node, collapse)
29830     {
29831         var nodeRange = node.ownerDocument.createRange();
29832         try {
29833             nodeRange.selectNode(node);
29834         } catch (e) {
29835             nodeRange.selectNodeContents(node);
29836         }
29837         if (collapse === true) {
29838             nodeRange.collapse(true);
29839         }
29840         //
29841         var s = this.win.getSelection();
29842         s.removeAllRanges();
29843         s.addRange(nodeRange);
29844     },
29845     
29846     getSelectedNode: function() 
29847     {
29848         // this may only work on Gecko!!!
29849         
29850         // should we cache this!!!!
29851         
29852          
29853          
29854         var range = this.createRange(this.getSelection()).cloneRange();
29855         
29856         if (Roo.isIE) {
29857             var parent = range.parentElement();
29858             while (true) {
29859                 var testRange = range.duplicate();
29860                 testRange.moveToElementText(parent);
29861                 if (testRange.inRange(range)) {
29862                     break;
29863                 }
29864                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
29865                     break;
29866                 }
29867                 parent = parent.parentElement;
29868             }
29869             return parent;
29870         }
29871         
29872         // is ancestor a text element.
29873         var ac =  range.commonAncestorContainer;
29874         if (ac.nodeType == 3) {
29875             ac = ac.parentNode;
29876         }
29877         
29878         var ar = ac.childNodes;
29879          
29880         var nodes = [];
29881         var other_nodes = [];
29882         var has_other_nodes = false;
29883         for (var i=0;i<ar.length;i++) {
29884             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
29885                 continue;
29886             }
29887             // fullly contained node.
29888             
29889             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
29890                 nodes.push(ar[i]);
29891                 continue;
29892             }
29893             
29894             // probably selected..
29895             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
29896                 other_nodes.push(ar[i]);
29897                 continue;
29898             }
29899             // outer..
29900             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
29901                 continue;
29902             }
29903             
29904             
29905             has_other_nodes = true;
29906         }
29907         if (!nodes.length && other_nodes.length) {
29908             nodes= other_nodes;
29909         }
29910         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
29911             return false;
29912         }
29913         
29914         return nodes[0];
29915     },
29916     
29917     
29918     createRange: function(sel)
29919     {
29920         // this has strange effects when using with 
29921         // top toolbar - not sure if it's a great idea.
29922         //this.editor.contentWindow.focus();
29923         if (typeof sel != "undefined") {
29924             try {
29925                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
29926             } catch(e) {
29927                 return this.doc.createRange();
29928             }
29929         } else {
29930             return this.doc.createRange();
29931         }
29932     },
29933     getParentElement: function()
29934     {
29935         
29936         this.assignDocWin();
29937         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
29938         
29939         var range = this.createRange(sel);
29940          
29941         try {
29942             var p = range.commonAncestorContainer;
29943             while (p.nodeType == 3) { // text node
29944                 p = p.parentNode;
29945             }
29946             return p;
29947         } catch (e) {
29948             return null;
29949         }
29950     
29951     },
29952     /***
29953      *
29954      * Range intersection.. the hard stuff...
29955      *  '-1' = before
29956      *  '0' = hits..
29957      *  '1' = after.
29958      *         [ -- selected range --- ]
29959      *   [fail]                        [fail]
29960      *
29961      *    basically..
29962      *      if end is before start or  hits it. fail.
29963      *      if start is after end or hits it fail.
29964      *
29965      *   if either hits (but other is outside. - then it's not 
29966      *   
29967      *    
29968      **/
29969     
29970     
29971     // @see http://www.thismuchiknow.co.uk/?p=64.
29972     rangeIntersectsNode : function(range, node)
29973     {
29974         var nodeRange = node.ownerDocument.createRange();
29975         try {
29976             nodeRange.selectNode(node);
29977         } catch (e) {
29978             nodeRange.selectNodeContents(node);
29979         }
29980     
29981         var rangeStartRange = range.cloneRange();
29982         rangeStartRange.collapse(true);
29983     
29984         var rangeEndRange = range.cloneRange();
29985         rangeEndRange.collapse(false);
29986     
29987         var nodeStartRange = nodeRange.cloneRange();
29988         nodeStartRange.collapse(true);
29989     
29990         var nodeEndRange = nodeRange.cloneRange();
29991         nodeEndRange.collapse(false);
29992     
29993         return rangeStartRange.compareBoundaryPoints(
29994                  Range.START_TO_START, nodeEndRange) == -1 &&
29995                rangeEndRange.compareBoundaryPoints(
29996                  Range.START_TO_START, nodeStartRange) == 1;
29997         
29998          
29999     },
30000     rangeCompareNode : function(range, node)
30001     {
30002         var nodeRange = node.ownerDocument.createRange();
30003         try {
30004             nodeRange.selectNode(node);
30005         } catch (e) {
30006             nodeRange.selectNodeContents(node);
30007         }
30008         
30009         
30010         range.collapse(true);
30011     
30012         nodeRange.collapse(true);
30013      
30014         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
30015         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
30016          
30017         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
30018         
30019         var nodeIsBefore   =  ss == 1;
30020         var nodeIsAfter    = ee == -1;
30021         
30022         if (nodeIsBefore && nodeIsAfter) {
30023             return 0; // outer
30024         }
30025         if (!nodeIsBefore && nodeIsAfter) {
30026             return 1; //right trailed.
30027         }
30028         
30029         if (nodeIsBefore && !nodeIsAfter) {
30030             return 2;  // left trailed.
30031         }
30032         // fully contined.
30033         return 3;
30034     },
30035  
30036     cleanWordChars : function(input) {// change the chars to hex code
30037         
30038        var swapCodes  = [ 
30039             [    8211, "&#8211;" ], 
30040             [    8212, "&#8212;" ], 
30041             [    8216,  "'" ],  
30042             [    8217, "'" ],  
30043             [    8220, '"' ],  
30044             [    8221, '"' ],  
30045             [    8226, "*" ],  
30046             [    8230, "..." ]
30047         ]; 
30048         var output = input;
30049         Roo.each(swapCodes, function(sw) { 
30050             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
30051             
30052             output = output.replace(swapper, sw[1]);
30053         });
30054         
30055         return output;
30056     },
30057     
30058      
30059     
30060         
30061     
30062     cleanUpChild : function (node)
30063     {
30064         
30065         new Roo.htmleditor.FilterComment({node : node});
30066         new Roo.htmleditor.FilterAttributes({
30067                 node : node,
30068                 attrib_black : this.ablack,
30069                 attrib_clean : this.aclean,
30070                 style_white : this.cwhite,
30071                 style_black : this.cblack
30072         });
30073         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
30074         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
30075          
30076         
30077     },
30078     
30079     /**
30080      * Clean up MS wordisms...
30081      * @deprecated - use filter directly
30082      */
30083     cleanWord : function(node)
30084     {
30085         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
30086         
30087     },
30088    
30089     
30090     /**
30091
30092      * @deprecated - use filters
30093      */
30094     cleanTableWidths : function(node)
30095     {
30096         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
30097         
30098  
30099     },
30100     
30101      
30102         
30103     applyBlacklists : function()
30104     {
30105         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
30106         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
30107         
30108         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
30109         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
30110         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
30111         
30112         this.white = [];
30113         this.black = [];
30114         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
30115             if (b.indexOf(tag) > -1) {
30116                 return;
30117             }
30118             this.white.push(tag);
30119             
30120         }, this);
30121         
30122         Roo.each(w, function(tag) {
30123             if (b.indexOf(tag) > -1) {
30124                 return;
30125             }
30126             if (this.white.indexOf(tag) > -1) {
30127                 return;
30128             }
30129             this.white.push(tag);
30130             
30131         }, this);
30132         
30133         
30134         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
30135             if (w.indexOf(tag) > -1) {
30136                 return;
30137             }
30138             this.black.push(tag);
30139             
30140         }, this);
30141         
30142         Roo.each(b, function(tag) {
30143             if (w.indexOf(tag) > -1) {
30144                 return;
30145             }
30146             if (this.black.indexOf(tag) > -1) {
30147                 return;
30148             }
30149             this.black.push(tag);
30150             
30151         }, this);
30152         
30153         
30154         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
30155         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
30156         
30157         this.cwhite = [];
30158         this.cblack = [];
30159         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
30160             if (b.indexOf(tag) > -1) {
30161                 return;
30162             }
30163             this.cwhite.push(tag);
30164             
30165         }, this);
30166         
30167         Roo.each(w, function(tag) {
30168             if (b.indexOf(tag) > -1) {
30169                 return;
30170             }
30171             if (this.cwhite.indexOf(tag) > -1) {
30172                 return;
30173             }
30174             this.cwhite.push(tag);
30175             
30176         }, this);
30177         
30178         
30179         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
30180             if (w.indexOf(tag) > -1) {
30181                 return;
30182             }
30183             this.cblack.push(tag);
30184             
30185         }, this);
30186         
30187         Roo.each(b, function(tag) {
30188             if (w.indexOf(tag) > -1) {
30189                 return;
30190             }
30191             if (this.cblack.indexOf(tag) > -1) {
30192                 return;
30193             }
30194             this.cblack.push(tag);
30195             
30196         }, this);
30197     },
30198     
30199     setStylesheets : function(stylesheets)
30200     {
30201         if(typeof(stylesheets) == 'string'){
30202             Roo.get(this.iframe.contentDocument.head).createChild({
30203                 tag : 'link',
30204                 rel : 'stylesheet',
30205                 type : 'text/css',
30206                 href : stylesheets
30207             });
30208             
30209             return;
30210         }
30211         var _this = this;
30212      
30213         Roo.each(stylesheets, function(s) {
30214             if(!s.length){
30215                 return;
30216             }
30217             
30218             Roo.get(_this.iframe.contentDocument.head).createChild({
30219                 tag : 'link',
30220                 rel : 'stylesheet',
30221                 type : 'text/css',
30222                 href : s
30223             });
30224         });
30225
30226         
30227     },
30228     
30229     
30230     updateLanguage : function()
30231     {
30232         if (!this.iframe || !this.iframe.contentDocument) {
30233             return;
30234         }
30235         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
30236     },
30237     
30238     
30239     removeStylesheets : function()
30240     {
30241         var _this = this;
30242         
30243         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
30244             s.remove();
30245         });
30246     },
30247     
30248     setStyle : function(style)
30249     {
30250         Roo.get(this.iframe.contentDocument.head).createChild({
30251             tag : 'style',
30252             type : 'text/css',
30253             html : style
30254         });
30255
30256         return;
30257     }
30258     
30259     // hide stuff that is not compatible
30260     /**
30261      * @event blur
30262      * @hide
30263      */
30264     /**
30265      * @event change
30266      * @hide
30267      */
30268     /**
30269      * @event focus
30270      * @hide
30271      */
30272     /**
30273      * @event specialkey
30274      * @hide
30275      */
30276     /**
30277      * @cfg {String} fieldClass @hide
30278      */
30279     /**
30280      * @cfg {String} focusClass @hide
30281      */
30282     /**
30283      * @cfg {String} autoCreate @hide
30284      */
30285     /**
30286      * @cfg {String} inputType @hide
30287      */
30288     /**
30289      * @cfg {String} invalidClass @hide
30290      */
30291     /**
30292      * @cfg {String} invalidText @hide
30293      */
30294     /**
30295      * @cfg {String} msgFx @hide
30296      */
30297     /**
30298      * @cfg {String} validateOnBlur @hide
30299      */
30300 });
30301
30302 Roo.HtmlEditorCore.white = [
30303         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
30304         
30305        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
30306        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
30307        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
30308        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
30309        'TABLE',   'UL',         'XMP', 
30310        
30311        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
30312       'THEAD',   'TR', 
30313      
30314       'DIR', 'MENU', 'OL', 'UL', 'DL',
30315        
30316       'EMBED',  'OBJECT'
30317 ];
30318
30319
30320 Roo.HtmlEditorCore.black = [
30321     //    'embed',  'object', // enable - backend responsiblity to clean thiese
30322         'APPLET', // 
30323         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
30324         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
30325         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
30326         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
30327         //'FONT' // CLEAN LATER..
30328         'COLGROUP', 'COL'   // messy tables.
30329         
30330         
30331 ];
30332 Roo.HtmlEditorCore.clean = [ // ?? needed???
30333      'SCRIPT', 'STYLE', 'TITLE', 'XML'
30334 ];
30335 Roo.HtmlEditorCore.tag_remove = [
30336     'FONT', 'TBODY'  
30337 ];
30338 // attributes..
30339
30340 Roo.HtmlEditorCore.ablack = [
30341     'on'
30342 ];
30343     
30344 Roo.HtmlEditorCore.aclean = [ 
30345     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
30346 ];
30347
30348 // protocols..
30349 Roo.HtmlEditorCore.pwhite= [
30350         'http',  'https',  'mailto'
30351 ];
30352
30353 // white listed style attributes.
30354 Roo.HtmlEditorCore.cwhite= [
30355       //  'text-align', /// default is to allow most things..
30356       
30357          
30358 //        'font-size'//??
30359 ];
30360
30361 // black listed style attributes.
30362 Roo.HtmlEditorCore.cblack= [
30363       //  'font-size' -- this can be set by the project 
30364 ];
30365
30366
30367
30368
30369     /*
30370  * - LGPL
30371  *
30372  * HtmlEditor
30373  * 
30374  */
30375
30376 /**
30377  * @class Roo.bootstrap.form.HtmlEditor
30378  * @extends Roo.bootstrap.form.TextArea
30379  * Bootstrap HtmlEditor class
30380
30381  * @constructor
30382  * Create a new HtmlEditor
30383  * @param {Object} config The config object
30384  */
30385
30386 Roo.bootstrap.form.HtmlEditor = function(config){
30387     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
30388     if (!this.toolbars) {
30389         this.toolbars = [];
30390     }
30391     
30392     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
30393     this.addEvents({
30394             /**
30395              * @event initialize
30396              * Fires when the editor is fully initialized (including the iframe)
30397              * @param {HtmlEditor} this
30398              */
30399             initialize: true,
30400             /**
30401              * @event activate
30402              * Fires when the editor is first receives the focus. Any insertion must wait
30403              * until after this event.
30404              * @param {HtmlEditor} this
30405              */
30406             activate: true,
30407              /**
30408              * @event beforesync
30409              * Fires before the textarea is updated with content from the editor iframe. Return false
30410              * to cancel the sync.
30411              * @param {HtmlEditor} this
30412              * @param {String} html
30413              */
30414             beforesync: true,
30415              /**
30416              * @event beforepush
30417              * Fires before the iframe editor is updated with content from the textarea. Return false
30418              * to cancel the push.
30419              * @param {HtmlEditor} this
30420              * @param {String} html
30421              */
30422             beforepush: true,
30423              /**
30424              * @event sync
30425              * Fires when the textarea is updated with content from the editor iframe.
30426              * @param {HtmlEditor} this
30427              * @param {String} html
30428              */
30429             sync: true,
30430              /**
30431              * @event push
30432              * Fires when the iframe editor is updated with content from the textarea.
30433              * @param {HtmlEditor} this
30434              * @param {String} html
30435              */
30436             push: true,
30437              /**
30438              * @event editmodechange
30439              * Fires when the editor switches edit modes
30440              * @param {HtmlEditor} this
30441              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
30442              */
30443             editmodechange: true,
30444             /**
30445              * @event editorevent
30446              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30447              * @param {HtmlEditor} this
30448              */
30449             editorevent: true,
30450             /**
30451              * @event firstfocus
30452              * Fires when on first focus - needed by toolbars..
30453              * @param {HtmlEditor} this
30454              */
30455             firstfocus: true,
30456             /**
30457              * @event autosave
30458              * Auto save the htmlEditor value as a file into Events
30459              * @param {HtmlEditor} this
30460              */
30461             autosave: true,
30462             /**
30463              * @event savedpreview
30464              * preview the saved version of htmlEditor
30465              * @param {HtmlEditor} this
30466              */
30467             savedpreview: true
30468         });
30469 };
30470
30471
30472 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
30473     
30474     
30475       /**
30476      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
30477      */
30478     toolbars : false,
30479     
30480      /**
30481     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
30482     */
30483     btns : [],
30484    
30485      /**
30486      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
30487      *                        Roo.resizable.
30488      */
30489     resizable : false,
30490      /**
30491      * @cfg {Number} height (in pixels)
30492      */   
30493     height: 300,
30494    /**
30495      * @cfg {Number} width (in pixels)
30496      */   
30497     width: false,
30498     
30499     /**
30500      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30501      * 
30502      */
30503     stylesheets: false,
30504     
30505     // id of frame..
30506     frameId: false,
30507     
30508     // private properties
30509     validationEvent : false,
30510     deferHeight: true,
30511     initialized : false,
30512     activated : false,
30513     
30514     onFocus : Roo.emptyFn,
30515     iframePad:3,
30516     hideMode:'offsets',
30517     
30518     tbContainer : false,
30519     
30520     bodyCls : '',
30521     
30522     toolbarContainer :function() {
30523         return this.wrap.select('.x-html-editor-tb',true).first();
30524     },
30525
30526     /**
30527      * Protected method that will not generally be called directly. It
30528      * is called when the editor creates its toolbar. Override this method if you need to
30529      * add custom toolbar buttons.
30530      * @param {HtmlEditor} editor
30531      */
30532     createToolbar : function(){
30533         Roo.log('renewing');
30534         Roo.log("create toolbars");
30535         
30536         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
30537         this.toolbars[0].render(this.toolbarContainer());
30538         
30539         return;
30540         
30541 //        if (!editor.toolbars || !editor.toolbars.length) {
30542 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
30543 //        }
30544 //        
30545 //        for (var i =0 ; i < editor.toolbars.length;i++) {
30546 //            editor.toolbars[i] = Roo.factory(
30547 //                    typeof(editor.toolbars[i]) == 'string' ?
30548 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
30549 //                Roo.bootstrap.form.HtmlEditor);
30550 //            editor.toolbars[i].init(editor);
30551 //        }
30552     },
30553
30554      
30555     // private
30556     onRender : function(ct, position)
30557     {
30558        // Roo.log("Call onRender: " + this.xtype);
30559         var _t = this;
30560         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
30561       
30562         this.wrap = this.inputEl().wrap({
30563             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
30564         });
30565         
30566         this.editorcore.onRender(ct, position);
30567          
30568         if (this.resizable) {
30569             this.resizeEl = new Roo.Resizable(this.wrap, {
30570                 pinned : true,
30571                 wrap: true,
30572                 dynamic : true,
30573                 minHeight : this.height,
30574                 height: this.height,
30575                 handles : this.resizable,
30576                 width: this.width,
30577                 listeners : {
30578                     resize : function(r, w, h) {
30579                         _t.onResize(w,h); // -something
30580                     }
30581                 }
30582             });
30583             
30584         }
30585         this.createToolbar(this);
30586        
30587         
30588         if(!this.width && this.resizable){
30589             this.setSize(this.wrap.getSize());
30590         }
30591         if (this.resizeEl) {
30592             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
30593             // should trigger onReize..
30594         }
30595         
30596     },
30597
30598     // private
30599     onResize : function(w, h)
30600     {
30601         Roo.log('resize: ' +w + ',' + h );
30602         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
30603         var ew = false;
30604         var eh = false;
30605         
30606         if(this.inputEl() ){
30607             if(typeof w == 'number'){
30608                 var aw = w - this.wrap.getFrameWidth('lr');
30609                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
30610                 ew = aw;
30611             }
30612             if(typeof h == 'number'){
30613                  var tbh = -11;  // fixme it needs to tool bar size!
30614                 for (var i =0; i < this.toolbars.length;i++) {
30615                     // fixme - ask toolbars for heights?
30616                     tbh += this.toolbars[i].el.getHeight();
30617                     //if (this.toolbars[i].footer) {
30618                     //    tbh += this.toolbars[i].footer.el.getHeight();
30619                     //}
30620                 }
30621               
30622                 
30623                 
30624                 
30625                 
30626                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
30627                 ah -= 5; // knock a few pixes off for look..
30628                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
30629                 var eh = ah;
30630             }
30631         }
30632         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
30633         this.editorcore.onResize(ew,eh);
30634         
30635     },
30636
30637     /**
30638      * Toggles the editor between standard and source edit mode.
30639      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30640      */
30641     toggleSourceEdit : function(sourceEditMode)
30642     {
30643         this.editorcore.toggleSourceEdit(sourceEditMode);
30644         
30645         if(this.editorcore.sourceEditMode){
30646             Roo.log('editor - showing textarea');
30647             
30648 //            Roo.log('in');
30649 //            Roo.log(this.syncValue());
30650             this.syncValue();
30651             this.inputEl().removeClass(['hide', 'x-hidden']);
30652             this.inputEl().dom.removeAttribute('tabIndex');
30653             this.inputEl().focus();
30654         }else{
30655             Roo.log('editor - hiding textarea');
30656 //            Roo.log('out')
30657 //            Roo.log(this.pushValue()); 
30658             this.pushValue();
30659             
30660             this.inputEl().addClass(['hide', 'x-hidden']);
30661             this.inputEl().dom.setAttribute('tabIndex', -1);
30662             //this.deferFocus();
30663         }
30664          
30665         if(this.resizable){
30666             this.setSize(this.wrap.getSize());
30667         }
30668         
30669         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
30670     },
30671  
30672     // private (for BoxComponent)
30673     adjustSize : Roo.BoxComponent.prototype.adjustSize,
30674
30675     // private (for BoxComponent)
30676     getResizeEl : function(){
30677         return this.wrap;
30678     },
30679
30680     // private (for BoxComponent)
30681     getPositionEl : function(){
30682         return this.wrap;
30683     },
30684
30685     // private
30686     initEvents : function(){
30687         this.originalValue = this.getValue();
30688     },
30689
30690 //    /**
30691 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
30692 //     * @method
30693 //     */
30694 //    markInvalid : Roo.emptyFn,
30695 //    /**
30696 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
30697 //     * @method
30698 //     */
30699 //    clearInvalid : Roo.emptyFn,
30700
30701     setValue : function(v){
30702         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
30703         this.editorcore.pushValue();
30704     },
30705
30706      
30707     // private
30708     deferFocus : function(){
30709         this.focus.defer(10, this);
30710     },
30711
30712     // doc'ed in Field
30713     focus : function(){
30714         this.editorcore.focus();
30715         
30716     },
30717       
30718
30719     // private
30720     onDestroy : function(){
30721         
30722         
30723         
30724         if(this.rendered){
30725             
30726             for (var i =0; i < this.toolbars.length;i++) {
30727                 // fixme - ask toolbars for heights?
30728                 this.toolbars[i].onDestroy();
30729             }
30730             
30731             this.wrap.dom.innerHTML = '';
30732             this.wrap.remove();
30733         }
30734     },
30735
30736     // private
30737     onFirstFocus : function(){
30738         //Roo.log("onFirstFocus");
30739         this.editorcore.onFirstFocus();
30740          for (var i =0; i < this.toolbars.length;i++) {
30741             this.toolbars[i].onFirstFocus();
30742         }
30743         
30744     },
30745     
30746     // private
30747     syncValue : function()
30748     {   
30749         this.editorcore.syncValue();
30750     },
30751     
30752     pushValue : function()
30753     {   
30754         this.editorcore.pushValue();
30755     }
30756      
30757     
30758     // hide stuff that is not compatible
30759     /**
30760      * @event blur
30761      * @hide
30762      */
30763     /**
30764      * @event change
30765      * @hide
30766      */
30767     /**
30768      * @event focus
30769      * @hide
30770      */
30771     /**
30772      * @event specialkey
30773      * @hide
30774      */
30775     /**
30776      * @cfg {String} fieldClass @hide
30777      */
30778     /**
30779      * @cfg {String} focusClass @hide
30780      */
30781     /**
30782      * @cfg {String} autoCreate @hide
30783      */
30784     /**
30785      * @cfg {String} inputType @hide
30786      */
30787      
30788     /**
30789      * @cfg {String} invalidText @hide
30790      */
30791     /**
30792      * @cfg {String} msgFx @hide
30793      */
30794     /**
30795      * @cfg {String} validateOnBlur @hide
30796      */
30797 });
30798  
30799     
30800    
30801    
30802    
30803       
30804 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
30805 /**
30806  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
30807  * @parent Roo.bootstrap.form.HtmlEditor
30808  * @extends Roo.bootstrap.nav.Simplebar
30809  * Basic Toolbar
30810  * 
30811  * @example
30812  * Usage:
30813  *
30814  new Roo.bootstrap.form.HtmlEditor({
30815     ....
30816     toolbars : [
30817         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
30818             disable : { fonts: 1 , format: 1, ..., ... , ...],
30819             btns : [ .... ]
30820         })
30821     }
30822      
30823  * 
30824  * @cfg {Object} disable List of elements to disable..
30825  * @cfg {Array} btns List of additional buttons.
30826  * 
30827  * 
30828  * NEEDS Extra CSS? 
30829  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
30830  */
30831  
30832 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
30833 {
30834     
30835     Roo.apply(this, config);
30836     
30837     // default disabled, based on 'good practice'..
30838     this.disable = this.disable || {};
30839     Roo.applyIf(this.disable, {
30840         fontSize : true,
30841         colors : true,
30842         specialElements : true
30843     });
30844     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
30845     
30846     this.editor = config.editor;
30847     this.editorcore = config.editor.editorcore;
30848     
30849     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
30850     
30851     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
30852     // dont call parent... till later.
30853 }
30854 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
30855      
30856     bar : true,
30857     
30858     editor : false,
30859     editorcore : false,
30860     
30861     
30862     formats : [
30863         "p" ,  
30864         "h1","h2","h3","h4","h5","h6", 
30865         "pre", "code", 
30866         "abbr", "acronym", "address", "cite", "samp", "var",
30867         'div','span'
30868     ],
30869     
30870     onRender : function(ct, position)
30871     {
30872        // Roo.log("Call onRender: " + this.xtype);
30873         
30874        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
30875        Roo.log(this.el);
30876        this.el.dom.style.marginBottom = '0';
30877        var _this = this;
30878        var editorcore = this.editorcore;
30879        var editor= this.editor;
30880        
30881        var children = [];
30882        var btn = function(id,cmd , toggle, handler, html){
30883        
30884             var  event = toggle ? 'toggle' : 'click';
30885        
30886             var a = {
30887                 size : 'sm',
30888                 xtype: 'Button',
30889                 xns: Roo.bootstrap,
30890                 //glyphicon : id,
30891                 fa: id,
30892                 cmd : id || cmd,
30893                 enableToggle:toggle !== false,
30894                 html : html || '',
30895                 pressed : toggle ? false : null,
30896                 listeners : {}
30897             };
30898             a.listeners[toggle ? 'toggle' : 'click'] = function() {
30899                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
30900             };
30901             children.push(a);
30902             return a;
30903        }
30904        
30905     //    var cb_box = function...
30906         
30907         var style = {
30908                 xtype: 'Button',
30909                 size : 'sm',
30910                 xns: Roo.bootstrap,
30911                 fa : 'font',
30912                 //html : 'submit'
30913                 menu : {
30914                     xtype: 'Menu',
30915                     xns: Roo.bootstrap,
30916                     items:  []
30917                 }
30918         };
30919         Roo.each(this.formats, function(f) {
30920             style.menu.items.push({
30921                 xtype :'MenuItem',
30922                 xns: Roo.bootstrap,
30923                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
30924                 tagname : f,
30925                 listeners : {
30926                     click : function()
30927                     {
30928                         editorcore.insertTag(this.tagname);
30929                         editor.focus();
30930                     }
30931                 }
30932                 
30933             });
30934         });
30935         children.push(style);   
30936         
30937         btn('bold',false,true);
30938         btn('italic',false,true);
30939         btn('align-left', 'justifyleft',true);
30940         btn('align-center', 'justifycenter',true);
30941         btn('align-right' , 'justifyright',true);
30942         btn('link', false, false, function(btn) {
30943             //Roo.log("create link?");
30944             var url = prompt(this.createLinkText, this.defaultLinkValue);
30945             if(url && url != 'http:/'+'/'){
30946                 this.editorcore.relayCmd('createlink', url);
30947             }
30948         }),
30949         btn('list','insertunorderedlist',true);
30950         btn('pencil', false,true, function(btn){
30951                 Roo.log(this);
30952                 this.toggleSourceEdit(btn.pressed);
30953         });
30954         
30955         if (this.editor.btns.length > 0) {
30956             for (var i = 0; i<this.editor.btns.length; i++) {
30957                 children.push(this.editor.btns[i]);
30958             }
30959         }
30960         
30961         /*
30962         var cog = {
30963                 xtype: 'Button',
30964                 size : 'sm',
30965                 xns: Roo.bootstrap,
30966                 glyphicon : 'cog',
30967                 //html : 'submit'
30968                 menu : {
30969                     xtype: 'Menu',
30970                     xns: Roo.bootstrap,
30971                     items:  []
30972                 }
30973         };
30974         
30975         cog.menu.items.push({
30976             xtype :'MenuItem',
30977             xns: Roo.bootstrap,
30978             html : Clean styles,
30979             tagname : f,
30980             listeners : {
30981                 click : function()
30982                 {
30983                     editorcore.insertTag(this.tagname);
30984                     editor.focus();
30985                 }
30986             }
30987             
30988         });
30989        */
30990         
30991          
30992        this.xtype = 'NavSimplebar';
30993         
30994         for(var i=0;i< children.length;i++) {
30995             
30996             this.buttons.add(this.addxtypeChild(children[i]));
30997             
30998         }
30999         
31000         editor.on('editorevent', this.updateToolbar, this);
31001     },
31002     onBtnClick : function(id)
31003     {
31004        this.editorcore.relayCmd(id);
31005        this.editorcore.focus();
31006     },
31007     
31008     /**
31009      * Protected method that will not generally be called directly. It triggers
31010      * a toolbar update by reading the markup state of the current selection in the editor.
31011      */
31012     updateToolbar: function(){
31013
31014         if(!this.editorcore.activated){
31015             this.editor.onFirstFocus(); // is this neeed?
31016             return;
31017         }
31018
31019         var btns = this.buttons; 
31020         var doc = this.editorcore.doc;
31021         btns.get('bold').setActive(doc.queryCommandState('bold'));
31022         btns.get('italic').setActive(doc.queryCommandState('italic'));
31023         //btns.get('underline').setActive(doc.queryCommandState('underline'));
31024         
31025         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
31026         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
31027         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
31028         
31029         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
31030         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
31031          /*
31032         
31033         var ans = this.editorcore.getAllAncestors();
31034         if (this.formatCombo) {
31035             
31036             
31037             var store = this.formatCombo.store;
31038             this.formatCombo.setValue("");
31039             for (var i =0; i < ans.length;i++) {
31040                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
31041                     // select it..
31042                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
31043                     break;
31044                 }
31045             }
31046         }
31047         
31048         
31049         
31050         // hides menus... - so this cant be on a menu...
31051         Roo.bootstrap.MenuMgr.hideAll();
31052         */
31053         Roo.bootstrap.menu.Manager.hideAll();
31054         //this.editorsyncValue();
31055     },
31056     onFirstFocus: function() {
31057         this.buttons.each(function(item){
31058            item.enable();
31059         });
31060     },
31061     toggleSourceEdit : function(sourceEditMode){
31062         
31063           
31064         if(sourceEditMode){
31065             Roo.log("disabling buttons");
31066            this.buttons.each( function(item){
31067                 if(item.cmd != 'pencil'){
31068                     item.disable();
31069                 }
31070             });
31071           
31072         }else{
31073             Roo.log("enabling buttons");
31074             if(this.editorcore.initialized){
31075                 this.buttons.each( function(item){
31076                     item.enable();
31077                 });
31078             }
31079             
31080         }
31081         Roo.log("calling toggole on editor");
31082         // tell the editor that it's been pressed..
31083         this.editor.toggleSourceEdit(sourceEditMode);
31084        
31085     }
31086 });
31087
31088
31089
31090
31091  
31092 /*
31093  * - LGPL
31094  */
31095
31096 /**
31097  * @class Roo.bootstrap.form.Markdown
31098  * @extends Roo.bootstrap.form.TextArea
31099  * Bootstrap Showdown editable area
31100  * @cfg {string} content
31101  * 
31102  * @constructor
31103  * Create a new Showdown
31104  */
31105
31106 Roo.bootstrap.form.Markdown = function(config){
31107     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
31108    
31109 };
31110
31111 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
31112     
31113     editing :false,
31114     
31115     initEvents : function()
31116     {
31117         
31118         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
31119         this.markdownEl = this.el.createChild({
31120             cls : 'roo-markdown-area'
31121         });
31122         this.inputEl().addClass('d-none');
31123         if (this.getValue() == '') {
31124             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31125             
31126         } else {
31127             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31128         }
31129         this.markdownEl.on('click', this.toggleTextEdit, this);
31130         this.on('blur', this.toggleTextEdit, this);
31131         this.on('specialkey', this.resizeTextArea, this);
31132     },
31133     
31134     toggleTextEdit : function()
31135     {
31136         var sh = this.markdownEl.getHeight();
31137         this.inputEl().addClass('d-none');
31138         this.markdownEl.addClass('d-none');
31139         if (!this.editing) {
31140             // show editor?
31141             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
31142             this.inputEl().removeClass('d-none');
31143             this.inputEl().focus();
31144             this.editing = true;
31145             return;
31146         }
31147         // show showdown...
31148         this.updateMarkdown();
31149         this.markdownEl.removeClass('d-none');
31150         this.editing = false;
31151         return;
31152     },
31153     updateMarkdown : function()
31154     {
31155         if (this.getValue() == '') {
31156             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31157             return;
31158         }
31159  
31160         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31161     },
31162     
31163     resizeTextArea: function () {
31164         
31165         var sh = 100;
31166         Roo.log([sh, this.getValue().split("\n").length * 30]);
31167         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
31168     },
31169     setValue : function(val)
31170     {
31171         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
31172         if (!this.editing) {
31173             this.updateMarkdown();
31174         }
31175         
31176     },
31177     focus : function()
31178     {
31179         if (!this.editing) {
31180             this.toggleTextEdit();
31181         }
31182         
31183     }
31184
31185
31186 });/*
31187  * Based on:
31188  * Ext JS Library 1.1.1
31189  * Copyright(c) 2006-2007, Ext JS, LLC.
31190  *
31191  * Originally Released Under LGPL - original licence link has changed is not relivant.
31192  *
31193  * Fork - LGPL
31194  * <script type="text/javascript">
31195  */
31196  
31197 /**
31198  * @class Roo.bootstrap.PagingToolbar
31199  * @extends Roo.bootstrap.nav.Simplebar
31200  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31201  * @constructor
31202  * Create a new PagingToolbar
31203  * @param {Object} config The config object
31204  * @param {Roo.data.Store} store
31205  */
31206 Roo.bootstrap.PagingToolbar = function(config)
31207 {
31208     // old args format still supported... - xtype is prefered..
31209         // created from xtype...
31210     
31211     this.ds = config.dataSource;
31212     
31213     if (config.store && !this.ds) {
31214         this.store= Roo.factory(config.store, Roo.data);
31215         this.ds = this.store;
31216         this.ds.xmodule = this.xmodule || false;
31217     }
31218     
31219     this.toolbarItems = [];
31220     if (config.items) {
31221         this.toolbarItems = config.items;
31222     }
31223     
31224     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
31225     
31226     this.cursor = 0;
31227     
31228     if (this.ds) { 
31229         this.bind(this.ds);
31230     }
31231     
31232     if (Roo.bootstrap.version == 4) {
31233         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
31234     } else {
31235         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
31236     }
31237     
31238 };
31239
31240 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
31241     /**
31242      * @cfg {Roo.bootstrap.Button} buttons[]
31243      * Buttons for the toolbar
31244      */
31245      /**
31246      * @cfg {Roo.data.Store} store
31247      * The underlying data store providing the paged data
31248      */
31249     /**
31250      * @cfg {String/HTMLElement/Element} container
31251      * container The id or element that will contain the toolbar
31252      */
31253     /**
31254      * @cfg {Boolean} displayInfo
31255      * True to display the displayMsg (defaults to false)
31256      */
31257     /**
31258      * @cfg {Number} pageSize
31259      * The number of records to display per page (defaults to 20)
31260      */
31261     pageSize: 20,
31262     /**
31263      * @cfg {String} displayMsg
31264      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31265      */
31266     displayMsg : 'Displaying {0} - {1} of {2}',
31267     /**
31268      * @cfg {String} emptyMsg
31269      * The message to display when no records are found (defaults to "No data to display")
31270      */
31271     emptyMsg : 'No data to display',
31272     /**
31273      * Customizable piece of the default paging text (defaults to "Page")
31274      * @type String
31275      */
31276     beforePageText : "Page",
31277     /**
31278      * Customizable piece of the default paging text (defaults to "of %0")
31279      * @type String
31280      */
31281     afterPageText : "of {0}",
31282     /**
31283      * Customizable piece of the default paging text (defaults to "First Page")
31284      * @type String
31285      */
31286     firstText : "First Page",
31287     /**
31288      * Customizable piece of the default paging text (defaults to "Previous Page")
31289      * @type String
31290      */
31291     prevText : "Previous Page",
31292     /**
31293      * Customizable piece of the default paging text (defaults to "Next Page")
31294      * @type String
31295      */
31296     nextText : "Next Page",
31297     /**
31298      * Customizable piece of the default paging text (defaults to "Last Page")
31299      * @type String
31300      */
31301     lastText : "Last Page",
31302     /**
31303      * Customizable piece of the default paging text (defaults to "Refresh")
31304      * @type String
31305      */
31306     refreshText : "Refresh",
31307
31308     buttons : false,
31309     // private
31310     onRender : function(ct, position) 
31311     {
31312         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
31313         this.navgroup.parentId = this.id;
31314         this.navgroup.onRender(this.el, null);
31315         // add the buttons to the navgroup
31316         
31317         if(this.displayInfo){
31318             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
31319             this.displayEl = this.el.select('.x-paging-info', true).first();
31320 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
31321 //            this.displayEl = navel.el.select('span',true).first();
31322         }
31323         
31324         var _this = this;
31325         
31326         if(this.buttons){
31327             Roo.each(_this.buttons, function(e){ // this might need to use render????
31328                Roo.factory(e).render(_this.el);
31329             });
31330         }
31331             
31332         Roo.each(_this.toolbarItems, function(e) {
31333             _this.navgroup.addItem(e);
31334         });
31335         
31336         
31337         this.first = this.navgroup.addItem({
31338             tooltip: this.firstText,
31339             cls: "prev btn-outline-secondary",
31340             html : ' <i class="fa fa-step-backward"></i>',
31341             disabled: true,
31342             preventDefault: true,
31343             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
31344         });
31345         
31346         this.prev =  this.navgroup.addItem({
31347             tooltip: this.prevText,
31348             cls: "prev btn-outline-secondary",
31349             html : ' <i class="fa fa-backward"></i>',
31350             disabled: true,
31351             preventDefault: true,
31352             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
31353         });
31354     //this.addSeparator();
31355         
31356         
31357         var field = this.navgroup.addItem( {
31358             tagtype : 'span',
31359             cls : 'x-paging-position  btn-outline-secondary',
31360              disabled: true,
31361             html : this.beforePageText  +
31362                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
31363                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
31364          } ); //?? escaped?
31365         
31366         this.field = field.el.select('input', true).first();
31367         this.field.on("keydown", this.onPagingKeydown, this);
31368         this.field.on("focus", function(){this.dom.select();});
31369     
31370     
31371         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
31372         //this.field.setHeight(18);
31373         //this.addSeparator();
31374         this.next = this.navgroup.addItem({
31375             tooltip: this.nextText,
31376             cls: "next btn-outline-secondary",
31377             html : ' <i class="fa fa-forward"></i>',
31378             disabled: true,
31379             preventDefault: true,
31380             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
31381         });
31382         this.last = this.navgroup.addItem({
31383             tooltip: this.lastText,
31384             html : ' <i class="fa fa-step-forward"></i>',
31385             cls: "next btn-outline-secondary",
31386             disabled: true,
31387             preventDefault: true,
31388             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
31389         });
31390     //this.addSeparator();
31391         this.loading = this.navgroup.addItem({
31392             tooltip: this.refreshText,
31393             cls: "btn-outline-secondary",
31394             html : ' <i class="fa fa-refresh"></i>',
31395             preventDefault: true,
31396             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
31397         });
31398         
31399     },
31400
31401     // private
31402     updateInfo : function(){
31403         if(this.displayEl){
31404             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
31405             var msg = count == 0 ?
31406                 this.emptyMsg :
31407                 String.format(
31408                     this.displayMsg,
31409                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31410                 );
31411             this.displayEl.update(msg);
31412         }
31413     },
31414
31415     // private
31416     onLoad : function(ds, r, o)
31417     {
31418         this.cursor = o.params && o.params.start ? o.params.start : 0;
31419         
31420         var d = this.getPageData(),
31421             ap = d.activePage,
31422             ps = d.pages;
31423         
31424         
31425         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
31426         this.field.dom.value = ap;
31427         this.first.setDisabled(ap == 1);
31428         this.prev.setDisabled(ap == 1);
31429         this.next.setDisabled(ap == ps);
31430         this.last.setDisabled(ap == ps);
31431         this.loading.enable();
31432         this.updateInfo();
31433     },
31434
31435     // private
31436     getPageData : function(){
31437         var total = this.ds.getTotalCount();
31438         return {
31439             total : total,
31440             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31441             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31442         };
31443     },
31444
31445     // private
31446     onLoadError : function(proxy, o){
31447         this.loading.enable();
31448         if (this.ds.events.loadexception.listeners.length  < 2) {
31449             // nothing has been assigned to loadexception except this...
31450             // so 
31451             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
31452
31453         }
31454     },
31455
31456     // private
31457     onPagingKeydown : function(e){
31458         var k = e.getKey();
31459         var d = this.getPageData();
31460         if(k == e.RETURN){
31461             var v = this.field.dom.value, pageNum;
31462             if(!v || isNaN(pageNum = parseInt(v, 10))){
31463                 this.field.dom.value = d.activePage;
31464                 return;
31465             }
31466             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31467             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31468             e.stopEvent();
31469         }
31470         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))
31471         {
31472           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31473           this.field.dom.value = pageNum;
31474           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31475           e.stopEvent();
31476         }
31477         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31478         {
31479           var v = this.field.dom.value, pageNum; 
31480           var increment = (e.shiftKey) ? 10 : 1;
31481           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31482                 increment *= -1;
31483           }
31484           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31485             this.field.dom.value = d.activePage;
31486             return;
31487           }
31488           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31489           {
31490             this.field.dom.value = parseInt(v, 10) + increment;
31491             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31492             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31493           }
31494           e.stopEvent();
31495         }
31496     },
31497
31498     // private
31499     beforeLoad : function(){
31500         if(this.loading){
31501             this.loading.disable();
31502         }
31503     },
31504
31505     // private
31506     onClick : function(which){
31507         
31508         var ds = this.ds;
31509         if (!ds) {
31510             return;
31511         }
31512         
31513         switch(which){
31514             case "first":
31515                 ds.load({params:{start: 0, limit: this.pageSize}});
31516             break;
31517             case "prev":
31518                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31519             break;
31520             case "next":
31521                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31522             break;
31523             case "last":
31524                 var total = ds.getTotalCount();
31525                 var extra = total % this.pageSize;
31526                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31527                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31528             break;
31529             case "refresh":
31530                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31531             break;
31532         }
31533     },
31534
31535     /**
31536      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31537      * @param {Roo.data.Store} store The data store to unbind
31538      */
31539     unbind : function(ds){
31540         ds.un("beforeload", this.beforeLoad, this);
31541         ds.un("load", this.onLoad, this);
31542         ds.un("loadexception", this.onLoadError, this);
31543         ds.un("remove", this.updateInfo, this);
31544         ds.un("add", this.updateInfo, this);
31545         this.ds = undefined;
31546     },
31547
31548     /**
31549      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31550      * @param {Roo.data.Store} store The data store to bind
31551      */
31552     bind : function(ds){
31553         ds.on("beforeload", this.beforeLoad, this);
31554         ds.on("load", this.onLoad, this);
31555         ds.on("loadexception", this.onLoadError, this);
31556         ds.on("remove", this.updateInfo, this);
31557         ds.on("add", this.updateInfo, this);
31558         this.ds = ds;
31559     }
31560 });/*
31561  * - LGPL
31562  *
31563  * element
31564  * 
31565  */
31566
31567 /**
31568  * @class Roo.bootstrap.MessageBar
31569  * @extends Roo.bootstrap.Component
31570  * Bootstrap MessageBar class
31571  * @cfg {String} html contents of the MessageBar
31572  * @cfg {String} weight (info | success | warning | danger) default info
31573  * @cfg {String} beforeClass insert the bar before the given class
31574  * @cfg {Boolean} closable (true | false) default false
31575  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
31576  * 
31577  * @constructor
31578  * Create a new Element
31579  * @param {Object} config The config object
31580  */
31581
31582 Roo.bootstrap.MessageBar = function(config){
31583     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
31584 };
31585
31586 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
31587     
31588     html: '',
31589     weight: 'info',
31590     closable: false,
31591     fixed: false,
31592     beforeClass: 'bootstrap-sticky-wrap',
31593     
31594     getAutoCreate : function(){
31595         
31596         var cfg = {
31597             tag: 'div',
31598             cls: 'alert alert-dismissable alert-' + this.weight,
31599             cn: [
31600                 {
31601                     tag: 'span',
31602                     cls: 'message',
31603                     html: this.html || ''
31604                 }
31605             ]
31606         };
31607         
31608         if(this.fixed){
31609             cfg.cls += ' alert-messages-fixed';
31610         }
31611         
31612         if(this.closable){
31613             cfg.cn.push({
31614                 tag: 'button',
31615                 cls: 'close',
31616                 html: 'x'
31617             });
31618         }
31619         
31620         return cfg;
31621     },
31622     
31623     onRender : function(ct, position)
31624     {
31625         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
31626         
31627         if(!this.el){
31628             var cfg = Roo.apply({},  this.getAutoCreate());
31629             cfg.id = Roo.id();
31630             
31631             if (this.cls) {
31632                 cfg.cls += ' ' + this.cls;
31633             }
31634             if (this.style) {
31635                 cfg.style = this.style;
31636             }
31637             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
31638             
31639             this.el.setVisibilityMode(Roo.Element.DISPLAY);
31640         }
31641         
31642         this.el.select('>button.close').on('click', this.hide, this);
31643         
31644     },
31645     
31646     show : function()
31647     {
31648         if (!this.rendered) {
31649             this.render();
31650         }
31651         
31652         this.el.show();
31653         
31654         this.fireEvent('show', this);
31655         
31656     },
31657     
31658     hide : function()
31659     {
31660         if (!this.rendered) {
31661             this.render();
31662         }
31663         
31664         this.el.hide();
31665         
31666         this.fireEvent('hide', this);
31667     },
31668     
31669     update : function()
31670     {
31671 //        var e = this.el.dom.firstChild;
31672 //        
31673 //        if(this.closable){
31674 //            e = e.nextSibling;
31675 //        }
31676 //        
31677 //        e.data = this.html || '';
31678
31679         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
31680     }
31681    
31682 });
31683
31684  
31685
31686      /*
31687  * - LGPL
31688  *
31689  * Graph
31690  * 
31691  */
31692
31693
31694 /**
31695  * @class Roo.bootstrap.Graph
31696  * @extends Roo.bootstrap.Component
31697  * Bootstrap Graph class
31698 > Prameters
31699  -sm {number} sm 4
31700  -md {number} md 5
31701  @cfg {String} graphtype  bar | vbar | pie
31702  @cfg {number} g_x coodinator | centre x (pie)
31703  @cfg {number} g_y coodinator | centre y (pie)
31704  @cfg {number} g_r radius (pie)
31705  @cfg {number} g_height height of the chart (respected by all elements in the set)
31706  @cfg {number} g_width width of the chart (respected by all elements in the set)
31707  @cfg {Object} title The title of the chart
31708     
31709  -{Array}  values
31710  -opts (object) options for the chart 
31711      o {
31712      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
31713      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
31714      o vgutter (number)
31715      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.
31716      o stacked (boolean) whether or not to tread values as in a stacked bar chart
31717      o to
31718      o stretch (boolean)
31719      o }
31720  -opts (object) options for the pie
31721      o{
31722      o cut
31723      o startAngle (number)
31724      o endAngle (number)
31725      } 
31726  *
31727  * @constructor
31728  * Create a new Input
31729  * @param {Object} config The config object
31730  */
31731
31732 Roo.bootstrap.Graph = function(config){
31733     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
31734     
31735     this.addEvents({
31736         // img events
31737         /**
31738          * @event click
31739          * The img click event for the img.
31740          * @param {Roo.EventObject} e
31741          */
31742         "click" : true
31743     });
31744 };
31745
31746 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
31747     
31748     sm: 4,
31749     md: 5,
31750     graphtype: 'bar',
31751     g_height: 250,
31752     g_width: 400,
31753     g_x: 50,
31754     g_y: 50,
31755     g_r: 30,
31756     opts:{
31757         //g_colors: this.colors,
31758         g_type: 'soft',
31759         g_gutter: '20%'
31760
31761     },
31762     title : false,
31763
31764     getAutoCreate : function(){
31765         
31766         var cfg = {
31767             tag: 'div',
31768             html : null
31769         };
31770         
31771         
31772         return  cfg;
31773     },
31774
31775     onRender : function(ct,position){
31776         
31777         
31778         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
31779         
31780         if (typeof(Raphael) == 'undefined') {
31781             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
31782             return;
31783         }
31784         
31785         this.raphael = Raphael(this.el.dom);
31786         
31787                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31788                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31789                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31790                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
31791                 /*
31792                 r.text(160, 10, "Single Series Chart").attr(txtattr);
31793                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
31794                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
31795                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
31796                 
31797                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
31798                 r.barchart(330, 10, 300, 220, data1);
31799                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
31800                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
31801                 */
31802                 
31803                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
31804                 // r.barchart(30, 30, 560, 250,  xdata, {
31805                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
31806                 //     axis : "0 0 1 1",
31807                 //     axisxlabels :  xdata
31808                 //     //yvalues : cols,
31809                    
31810                 // });
31811 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
31812 //        
31813 //        this.load(null,xdata,{
31814 //                axis : "0 0 1 1",
31815 //                axisxlabels :  xdata
31816 //                });
31817
31818     },
31819
31820     load : function(graphtype,xdata,opts)
31821     {
31822         this.raphael.clear();
31823         if(!graphtype) {
31824             graphtype = this.graphtype;
31825         }
31826         if(!opts){
31827             opts = this.opts;
31828         }
31829         var r = this.raphael,
31830             fin = function () {
31831                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
31832             },
31833             fout = function () {
31834                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
31835             },
31836             pfin = function() {
31837                 this.sector.stop();
31838                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
31839
31840                 if (this.label) {
31841                     this.label[0].stop();
31842                     this.label[0].attr({ r: 7.5 });
31843                     this.label[1].attr({ "font-weight": 800 });
31844                 }
31845             },
31846             pfout = function() {
31847                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
31848
31849                 if (this.label) {
31850                     this.label[0].animate({ r: 5 }, 500, "bounce");
31851                     this.label[1].attr({ "font-weight": 400 });
31852                 }
31853             };
31854
31855         switch(graphtype){
31856             case 'bar':
31857                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
31858                 break;
31859             case 'hbar':
31860                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
31861                 break;
31862             case 'pie':
31863 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
31864 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
31865 //            
31866                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
31867                 
31868                 break;
31869
31870         }
31871         
31872         if(this.title){
31873             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
31874         }
31875         
31876     },
31877     
31878     setTitle: function(o)
31879     {
31880         this.title = o;
31881     },
31882     
31883     initEvents: function() {
31884         
31885         if(!this.href){
31886             this.el.on('click', this.onClick, this);
31887         }
31888     },
31889     
31890     onClick : function(e)
31891     {
31892         Roo.log('img onclick');
31893         this.fireEvent('click', this, e);
31894     }
31895    
31896 });
31897
31898  
31899 Roo.bootstrap.dash = {};/*
31900  * - LGPL
31901  *
31902  * numberBox
31903  * 
31904  */
31905 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
31906
31907 /**
31908  * @class Roo.bootstrap.dash.NumberBox
31909  * @extends Roo.bootstrap.Component
31910  * Bootstrap NumberBox class
31911  * @cfg {String} headline Box headline
31912  * @cfg {String} content Box content
31913  * @cfg {String} icon Box icon
31914  * @cfg {String} footer Footer text
31915  * @cfg {String} fhref Footer href
31916  * 
31917  * @constructor
31918  * Create a new NumberBox
31919  * @param {Object} config The config object
31920  */
31921
31922
31923 Roo.bootstrap.dash.NumberBox = function(config){
31924     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
31925     
31926 };
31927
31928 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
31929     
31930     headline : '',
31931     content : '',
31932     icon : '',
31933     footer : '',
31934     fhref : '',
31935     ficon : '',
31936     
31937     getAutoCreate : function(){
31938         
31939         var cfg = {
31940             tag : 'div',
31941             cls : 'small-box ',
31942             cn : [
31943                 {
31944                     tag : 'div',
31945                     cls : 'inner',
31946                     cn :[
31947                         {
31948                             tag : 'h3',
31949                             cls : 'roo-headline',
31950                             html : this.headline
31951                         },
31952                         {
31953                             tag : 'p',
31954                             cls : 'roo-content',
31955                             html : this.content
31956                         }
31957                     ]
31958                 }
31959             ]
31960         };
31961         
31962         if(this.icon){
31963             cfg.cn.push({
31964                 tag : 'div',
31965                 cls : 'icon',
31966                 cn :[
31967                     {
31968                         tag : 'i',
31969                         cls : 'ion ' + this.icon
31970                     }
31971                 ]
31972             });
31973         }
31974         
31975         if(this.footer){
31976             var footer = {
31977                 tag : 'a',
31978                 cls : 'small-box-footer',
31979                 href : this.fhref || '#',
31980                 html : this.footer
31981             };
31982             
31983             cfg.cn.push(footer);
31984             
31985         }
31986         
31987         return  cfg;
31988     },
31989
31990     onRender : function(ct,position){
31991         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
31992
31993
31994        
31995                 
31996     },
31997
31998     setHeadline: function (value)
31999     {
32000         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
32001     },
32002     
32003     setFooter: function (value, href)
32004     {
32005         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
32006         
32007         if(href){
32008             this.el.select('a.small-box-footer',true).first().attr('href', href);
32009         }
32010         
32011     },
32012
32013     setContent: function (value)
32014     {
32015         this.el.select('.roo-content',true).first().dom.innerHTML = value;
32016     },
32017
32018     initEvents: function() 
32019     {   
32020         
32021     }
32022     
32023 });
32024
32025  
32026 /*
32027  * - LGPL
32028  *
32029  * TabBox
32030  * 
32031  */
32032 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32033
32034 /**
32035  * @class Roo.bootstrap.dash.TabBox
32036  * @extends Roo.bootstrap.Component
32037  * @children Roo.bootstrap.dash.TabPane
32038  * Bootstrap TabBox class
32039  * @cfg {String} title Title of the TabBox
32040  * @cfg {String} icon Icon of the TabBox
32041  * @cfg {Boolean} showtabs (true|false) show the tabs default true
32042  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
32043  * 
32044  * @constructor
32045  * Create a new TabBox
32046  * @param {Object} config The config object
32047  */
32048
32049
32050 Roo.bootstrap.dash.TabBox = function(config){
32051     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
32052     this.addEvents({
32053         // raw events
32054         /**
32055          * @event addpane
32056          * When a pane is added
32057          * @param {Roo.bootstrap.dash.TabPane} pane
32058          */
32059         "addpane" : true,
32060         /**
32061          * @event activatepane
32062          * When a pane is activated
32063          * @param {Roo.bootstrap.dash.TabPane} pane
32064          */
32065         "activatepane" : true
32066         
32067          
32068     });
32069     
32070     this.panes = [];
32071 };
32072
32073 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
32074
32075     title : '',
32076     icon : false,
32077     showtabs : true,
32078     tabScrollable : false,
32079     
32080     getChildContainer : function()
32081     {
32082         return this.el.select('.tab-content', true).first();
32083     },
32084     
32085     getAutoCreate : function(){
32086         
32087         var header = {
32088             tag: 'li',
32089             cls: 'pull-left header',
32090             html: this.title,
32091             cn : []
32092         };
32093         
32094         if(this.icon){
32095             header.cn.push({
32096                 tag: 'i',
32097                 cls: 'fa ' + this.icon
32098             });
32099         }
32100         
32101         var h = {
32102             tag: 'ul',
32103             cls: 'nav nav-tabs pull-right',
32104             cn: [
32105                 header
32106             ]
32107         };
32108         
32109         if(this.tabScrollable){
32110             h = {
32111                 tag: 'div',
32112                 cls: 'tab-header',
32113                 cn: [
32114                     {
32115                         tag: 'ul',
32116                         cls: 'nav nav-tabs pull-right',
32117                         cn: [
32118                             header
32119                         ]
32120                     }
32121                 ]
32122             };
32123         }
32124         
32125         var cfg = {
32126             tag: 'div',
32127             cls: 'nav-tabs-custom',
32128             cn: [
32129                 h,
32130                 {
32131                     tag: 'div',
32132                     cls: 'tab-content no-padding',
32133                     cn: []
32134                 }
32135             ]
32136         };
32137
32138         return  cfg;
32139     },
32140     initEvents : function()
32141     {
32142         //Roo.log('add add pane handler');
32143         this.on('addpane', this.onAddPane, this);
32144     },
32145      /**
32146      * Updates the box title
32147      * @param {String} html to set the title to.
32148      */
32149     setTitle : function(value)
32150     {
32151         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
32152     },
32153     onAddPane : function(pane)
32154     {
32155         this.panes.push(pane);
32156         //Roo.log('addpane');
32157         //Roo.log(pane);
32158         // tabs are rendere left to right..
32159         if(!this.showtabs){
32160             return;
32161         }
32162         
32163         var ctr = this.el.select('.nav-tabs', true).first();
32164          
32165          
32166         var existing = ctr.select('.nav-tab',true);
32167         var qty = existing.getCount();;
32168         
32169         
32170         var tab = ctr.createChild({
32171             tag : 'li',
32172             cls : 'nav-tab' + (qty ? '' : ' active'),
32173             cn : [
32174                 {
32175                     tag : 'a',
32176                     href:'#',
32177                     html : pane.title
32178                 }
32179             ]
32180         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
32181         pane.tab = tab;
32182         
32183         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
32184         if (!qty) {
32185             pane.el.addClass('active');
32186         }
32187         
32188                 
32189     },
32190     onTabClick : function(ev,un,ob,pane)
32191     {
32192         //Roo.log('tab - prev default');
32193         ev.preventDefault();
32194         
32195         
32196         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
32197         pane.tab.addClass('active');
32198         //Roo.log(pane.title);
32199         this.getChildContainer().select('.tab-pane',true).removeClass('active');
32200         // technically we should have a deactivate event.. but maybe add later.
32201         // and it should not de-activate the selected tab...
32202         this.fireEvent('activatepane', pane);
32203         pane.el.addClass('active');
32204         pane.fireEvent('activate');
32205         
32206         
32207     },
32208     
32209     getActivePane : function()
32210     {
32211         var r = false;
32212         Roo.each(this.panes, function(p) {
32213             if(p.el.hasClass('active')){
32214                 r = p;
32215                 return false;
32216             }
32217             
32218             return;
32219         });
32220         
32221         return r;
32222     }
32223     
32224     
32225 });
32226
32227  
32228 /*
32229  * - LGPL
32230  *
32231  * Tab pane
32232  * 
32233  */
32234 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32235 /**
32236  * @class Roo.bootstrap.TabPane
32237  * @extends Roo.bootstrap.Component
32238  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
32239  * Bootstrap TabPane class
32240  * @cfg {Boolean} active (false | true) Default false
32241  * @cfg {String} title title of panel
32242
32243  * 
32244  * @constructor
32245  * Create a new TabPane
32246  * @param {Object} config The config object
32247  */
32248
32249 Roo.bootstrap.dash.TabPane = function(config){
32250     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
32251     
32252     this.addEvents({
32253         // raw events
32254         /**
32255          * @event activate
32256          * When a pane is activated
32257          * @param {Roo.bootstrap.dash.TabPane} pane
32258          */
32259         "activate" : true
32260          
32261     });
32262 };
32263
32264 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
32265     
32266     active : false,
32267     title : '',
32268     
32269     // the tabBox that this is attached to.
32270     tab : false,
32271      
32272     getAutoCreate : function() 
32273     {
32274         var cfg = {
32275             tag: 'div',
32276             cls: 'tab-pane'
32277         };
32278         
32279         if(this.active){
32280             cfg.cls += ' active';
32281         }
32282         
32283         return cfg;
32284     },
32285     initEvents  : function()
32286     {
32287         //Roo.log('trigger add pane handler');
32288         this.parent().fireEvent('addpane', this)
32289     },
32290     
32291      /**
32292      * Updates the tab title 
32293      * @param {String} html to set the title to.
32294      */
32295     setTitle: function(str)
32296     {
32297         if (!this.tab) {
32298             return;
32299         }
32300         this.title = str;
32301         this.tab.select('a', true).first().dom.innerHTML = str;
32302         
32303     }
32304     
32305     
32306     
32307 });
32308
32309  
32310
32311
32312  /*
32313  * - LGPL
32314  *
32315  * Tooltip
32316  * 
32317  */
32318
32319 /**
32320  * @class Roo.bootstrap.Tooltip
32321  * Bootstrap Tooltip class
32322  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
32323  * to determine which dom element triggers the tooltip.
32324  * 
32325  * It needs to add support for additional attributes like tooltip-position
32326  * 
32327  * @constructor
32328  * Create a new Toolti
32329  * @param {Object} config The config object
32330  */
32331
32332 Roo.bootstrap.Tooltip = function(config){
32333     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
32334     
32335     this.alignment = Roo.bootstrap.Tooltip.alignment;
32336     
32337     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
32338         this.alignment = config.alignment;
32339     }
32340     
32341 };
32342
32343 Roo.apply(Roo.bootstrap.Tooltip, {
32344     /**
32345      * @function init initialize tooltip monitoring.
32346      * @static
32347      */
32348     currentEl : false,
32349     currentTip : false,
32350     currentRegion : false,
32351     
32352     //  init : delay?
32353     
32354     init : function()
32355     {
32356         Roo.get(document).on('mouseover', this.enter ,this);
32357         Roo.get(document).on('mouseout', this.leave, this);
32358          
32359         
32360         this.currentTip = new Roo.bootstrap.Tooltip();
32361     },
32362     
32363     enter : function(ev)
32364     {
32365         var dom = ev.getTarget();
32366         
32367         //Roo.log(['enter',dom]);
32368         var el = Roo.fly(dom);
32369         if (this.currentEl) {
32370             //Roo.log(dom);
32371             //Roo.log(this.currentEl);
32372             //Roo.log(this.currentEl.contains(dom));
32373             if (this.currentEl == el) {
32374                 return;
32375             }
32376             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
32377                 return;
32378             }
32379
32380         }
32381         
32382         if (this.currentTip.el) {
32383             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
32384         }    
32385         //Roo.log(ev);
32386         
32387         if(!el || el.dom == document){
32388             return;
32389         }
32390         
32391         var bindEl = el; 
32392         var pel = false;
32393         if (!el.attr('tooltip')) {
32394             pel = el.findParent("[tooltip]");
32395             if (pel) {
32396                 bindEl = Roo.get(pel);
32397             }
32398         }
32399         
32400        
32401         
32402         // you can not look for children, as if el is the body.. then everythign is the child..
32403         if (!pel && !el.attr('tooltip')) { //
32404             if (!el.select("[tooltip]").elements.length) {
32405                 return;
32406             }
32407             // is the mouse over this child...?
32408             bindEl = el.select("[tooltip]").first();
32409             var xy = ev.getXY();
32410             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
32411                 //Roo.log("not in region.");
32412                 return;
32413             }
32414             //Roo.log("child element over..");
32415             
32416         }
32417         this.currentEl = el;
32418         this.currentTip.bind(bindEl);
32419         this.currentRegion = Roo.lib.Region.getRegion(dom);
32420         this.currentTip.enter();
32421         
32422     },
32423     leave : function(ev)
32424     {
32425         var dom = ev.getTarget();
32426         //Roo.log(['leave',dom]);
32427         if (!this.currentEl) {
32428             return;
32429         }
32430         
32431         
32432         if (dom != this.currentEl.dom) {
32433             return;
32434         }
32435         var xy = ev.getXY();
32436         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
32437             return;
32438         }
32439         // only activate leave if mouse cursor is outside... bounding box..
32440         
32441         
32442         
32443         
32444         if (this.currentTip) {
32445             this.currentTip.leave();
32446         }
32447         //Roo.log('clear currentEl');
32448         this.currentEl = false;
32449         
32450         
32451     },
32452     alignment : {
32453         'left' : ['r-l', [-2,0], 'right'],
32454         'right' : ['l-r', [2,0], 'left'],
32455         'bottom' : ['t-b', [0,2], 'top'],
32456         'top' : [ 'b-t', [0,-2], 'bottom']
32457     }
32458     
32459 });
32460
32461
32462 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
32463     
32464     
32465     bindEl : false,
32466     
32467     delay : null, // can be { show : 300 , hide: 500}
32468     
32469     timeout : null,
32470     
32471     hoverState : null, //???
32472     
32473     placement : 'bottom', 
32474     
32475     alignment : false,
32476     
32477     getAutoCreate : function(){
32478     
32479         var cfg = {
32480            cls : 'tooltip',   
32481            role : 'tooltip',
32482            cn : [
32483                 {
32484                     cls : 'tooltip-arrow arrow'
32485                 },
32486                 {
32487                     cls : 'tooltip-inner'
32488                 }
32489            ]
32490         };
32491         
32492         return cfg;
32493     },
32494     bind : function(el)
32495     {
32496         this.bindEl = el;
32497     },
32498     
32499     initEvents : function()
32500     {
32501         this.arrowEl = this.el.select('.arrow', true).first();
32502         this.innerEl = this.el.select('.tooltip-inner', true).first();
32503     },
32504     
32505     enter : function () {
32506        
32507         if (this.timeout != null) {
32508             clearTimeout(this.timeout);
32509         }
32510         
32511         this.hoverState = 'in';
32512          //Roo.log("enter - show");
32513         if (!this.delay || !this.delay.show) {
32514             this.show();
32515             return;
32516         }
32517         var _t = this;
32518         this.timeout = setTimeout(function () {
32519             if (_t.hoverState == 'in') {
32520                 _t.show();
32521             }
32522         }, this.delay.show);
32523     },
32524     leave : function()
32525     {
32526         clearTimeout(this.timeout);
32527     
32528         this.hoverState = 'out';
32529          if (!this.delay || !this.delay.hide) {
32530             this.hide();
32531             return;
32532         }
32533        
32534         var _t = this;
32535         this.timeout = setTimeout(function () {
32536             //Roo.log("leave - timeout");
32537             
32538             if (_t.hoverState == 'out') {
32539                 _t.hide();
32540                 Roo.bootstrap.Tooltip.currentEl = false;
32541             }
32542         }, delay);
32543     },
32544     
32545     show : function (msg)
32546     {
32547         if (!this.el) {
32548             this.render(document.body);
32549         }
32550         // set content.
32551         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
32552         
32553         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
32554         
32555         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
32556         
32557         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
32558                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
32559         
32560         var placement = typeof this.placement == 'function' ?
32561             this.placement.call(this, this.el, on_el) :
32562             this.placement;
32563             
32564         var autoToken = /\s?auto?\s?/i;
32565         var autoPlace = autoToken.test(placement);
32566         if (autoPlace) {
32567             placement = placement.replace(autoToken, '') || 'top';
32568         }
32569         
32570         //this.el.detach()
32571         //this.el.setXY([0,0]);
32572         this.el.show();
32573         //this.el.dom.style.display='block';
32574         
32575         //this.el.appendTo(on_el);
32576         
32577         var p = this.getPosition();
32578         var box = this.el.getBox();
32579         
32580         if (autoPlace) {
32581             // fixme..
32582         }
32583         
32584         var align = this.alignment[placement];
32585         
32586         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
32587         
32588         if(placement == 'top' || placement == 'bottom'){
32589             if(xy[0] < 0){
32590                 placement = 'right';
32591             }
32592             
32593             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
32594                 placement = 'left';
32595             }
32596             
32597             var scroll = Roo.select('body', true).first().getScroll();
32598             
32599             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
32600                 placement = 'top';
32601             }
32602             
32603             align = this.alignment[placement];
32604             
32605             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
32606             
32607         }
32608         
32609         var elems = document.getElementsByTagName('div');
32610         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
32611         for (var i = 0; i < elems.length; i++) {
32612           var zindex = Number.parseInt(
32613                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
32614                 10
32615           );
32616           if (zindex > highest) {
32617             highest = zindex;
32618           }
32619         }
32620         
32621         
32622         
32623         this.el.dom.style.zIndex = highest;
32624         
32625         this.el.alignTo(this.bindEl, align[0],align[1]);
32626         //var arrow = this.el.select('.arrow',true).first();
32627         //arrow.set(align[2], 
32628         
32629         this.el.addClass(placement);
32630         this.el.addClass("bs-tooltip-"+ placement);
32631         
32632         this.el.addClass('in fade show');
32633         
32634         this.hoverState = null;
32635         
32636         if (this.el.hasClass('fade')) {
32637             // fade it?
32638         }
32639         
32640         
32641         
32642         
32643         
32644     },
32645     hide : function()
32646     {
32647          
32648         if (!this.el) {
32649             return;
32650         }
32651         //this.el.setXY([0,0]);
32652         this.el.removeClass(['show', 'in']);
32653         //this.el.hide();
32654         
32655     }
32656     
32657 });
32658  
32659
32660  /*
32661  * - LGPL
32662  *
32663  * Location Picker
32664  * 
32665  */
32666
32667 /**
32668  * @class Roo.bootstrap.LocationPicker
32669  * @extends Roo.bootstrap.Component
32670  * Bootstrap LocationPicker class
32671  * @cfg {Number} latitude Position when init default 0
32672  * @cfg {Number} longitude Position when init default 0
32673  * @cfg {Number} zoom default 15
32674  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
32675  * @cfg {Boolean} mapTypeControl default false
32676  * @cfg {Boolean} disableDoubleClickZoom default false
32677  * @cfg {Boolean} scrollwheel default true
32678  * @cfg {Boolean} streetViewControl default false
32679  * @cfg {Number} radius default 0
32680  * @cfg {String} locationName
32681  * @cfg {Boolean} draggable default true
32682  * @cfg {Boolean} enableAutocomplete default false
32683  * @cfg {Boolean} enableReverseGeocode default true
32684  * @cfg {String} markerTitle
32685  * 
32686  * @constructor
32687  * Create a new LocationPicker
32688  * @param {Object} config The config object
32689  */
32690
32691
32692 Roo.bootstrap.LocationPicker = function(config){
32693     
32694     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
32695     
32696     this.addEvents({
32697         /**
32698          * @event initial
32699          * Fires when the picker initialized.
32700          * @param {Roo.bootstrap.LocationPicker} this
32701          * @param {Google Location} location
32702          */
32703         initial : true,
32704         /**
32705          * @event positionchanged
32706          * Fires when the picker position changed.
32707          * @param {Roo.bootstrap.LocationPicker} this
32708          * @param {Google Location} location
32709          */
32710         positionchanged : true,
32711         /**
32712          * @event resize
32713          * Fires when the map resize.
32714          * @param {Roo.bootstrap.LocationPicker} this
32715          */
32716         resize : true,
32717         /**
32718          * @event show
32719          * Fires when the map show.
32720          * @param {Roo.bootstrap.LocationPicker} this
32721          */
32722         show : true,
32723         /**
32724          * @event hide
32725          * Fires when the map hide.
32726          * @param {Roo.bootstrap.LocationPicker} this
32727          */
32728         hide : true,
32729         /**
32730          * @event mapClick
32731          * Fires when click the map.
32732          * @param {Roo.bootstrap.LocationPicker} this
32733          * @param {Map event} e
32734          */
32735         mapClick : true,
32736         /**
32737          * @event mapRightClick
32738          * Fires when right click the map.
32739          * @param {Roo.bootstrap.LocationPicker} this
32740          * @param {Map event} e
32741          */
32742         mapRightClick : true,
32743         /**
32744          * @event markerClick
32745          * Fires when click the marker.
32746          * @param {Roo.bootstrap.LocationPicker} this
32747          * @param {Map event} e
32748          */
32749         markerClick : true,
32750         /**
32751          * @event markerRightClick
32752          * Fires when right click the marker.
32753          * @param {Roo.bootstrap.LocationPicker} this
32754          * @param {Map event} e
32755          */
32756         markerRightClick : true,
32757         /**
32758          * @event OverlayViewDraw
32759          * Fires when OverlayView Draw
32760          * @param {Roo.bootstrap.LocationPicker} this
32761          */
32762         OverlayViewDraw : true,
32763         /**
32764          * @event OverlayViewOnAdd
32765          * Fires when OverlayView Draw
32766          * @param {Roo.bootstrap.LocationPicker} this
32767          */
32768         OverlayViewOnAdd : true,
32769         /**
32770          * @event OverlayViewOnRemove
32771          * Fires when OverlayView Draw
32772          * @param {Roo.bootstrap.LocationPicker} this
32773          */
32774         OverlayViewOnRemove : true,
32775         /**
32776          * @event OverlayViewShow
32777          * Fires when OverlayView Draw
32778          * @param {Roo.bootstrap.LocationPicker} this
32779          * @param {Pixel} cpx
32780          */
32781         OverlayViewShow : true,
32782         /**
32783          * @event OverlayViewHide
32784          * Fires when OverlayView Draw
32785          * @param {Roo.bootstrap.LocationPicker} this
32786          */
32787         OverlayViewHide : true,
32788         /**
32789          * @event loadexception
32790          * Fires when load google lib failed.
32791          * @param {Roo.bootstrap.LocationPicker} this
32792          */
32793         loadexception : true
32794     });
32795         
32796 };
32797
32798 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
32799     
32800     gMapContext: false,
32801     
32802     latitude: 0,
32803     longitude: 0,
32804     zoom: 15,
32805     mapTypeId: false,
32806     mapTypeControl: false,
32807     disableDoubleClickZoom: false,
32808     scrollwheel: true,
32809     streetViewControl: false,
32810     radius: 0,
32811     locationName: '',
32812     draggable: true,
32813     enableAutocomplete: false,
32814     enableReverseGeocode: true,
32815     markerTitle: '',
32816     
32817     getAutoCreate: function()
32818     {
32819
32820         var cfg = {
32821             tag: 'div',
32822             cls: 'roo-location-picker'
32823         };
32824         
32825         return cfg
32826     },
32827     
32828     initEvents: function(ct, position)
32829     {       
32830         if(!this.el.getWidth() || this.isApplied()){
32831             return;
32832         }
32833         
32834         this.el.setVisibilityMode(Roo.Element.DISPLAY);
32835         
32836         this.initial();
32837     },
32838     
32839     initial: function()
32840     {
32841         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
32842             this.fireEvent('loadexception', this);
32843             return;
32844         }
32845         
32846         if(!this.mapTypeId){
32847             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
32848         }
32849         
32850         this.gMapContext = this.GMapContext();
32851         
32852         this.initOverlayView();
32853         
32854         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
32855         
32856         var _this = this;
32857                 
32858         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
32859             _this.setPosition(_this.gMapContext.marker.position);
32860         });
32861         
32862         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
32863             _this.fireEvent('mapClick', this, event);
32864             
32865         });
32866
32867         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
32868             _this.fireEvent('mapRightClick', this, event);
32869             
32870         });
32871         
32872         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
32873             _this.fireEvent('markerClick', this, event);
32874             
32875         });
32876
32877         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
32878             _this.fireEvent('markerRightClick', this, event);
32879             
32880         });
32881         
32882         this.setPosition(this.gMapContext.location);
32883         
32884         this.fireEvent('initial', this, this.gMapContext.location);
32885     },
32886     
32887     initOverlayView: function()
32888     {
32889         var _this = this;
32890         
32891         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
32892             
32893             draw: function()
32894             {
32895                 _this.fireEvent('OverlayViewDraw', _this);
32896             },
32897             
32898             onAdd: function()
32899             {
32900                 _this.fireEvent('OverlayViewOnAdd', _this);
32901             },
32902             
32903             onRemove: function()
32904             {
32905                 _this.fireEvent('OverlayViewOnRemove', _this);
32906             },
32907             
32908             show: function(cpx)
32909             {
32910                 _this.fireEvent('OverlayViewShow', _this, cpx);
32911             },
32912             
32913             hide: function()
32914             {
32915                 _this.fireEvent('OverlayViewHide', _this);
32916             }
32917             
32918         });
32919     },
32920     
32921     fromLatLngToContainerPixel: function(event)
32922     {
32923         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
32924     },
32925     
32926     isApplied: function() 
32927     {
32928         return this.getGmapContext() == false ? false : true;
32929     },
32930     
32931     getGmapContext: function() 
32932     {
32933         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
32934     },
32935     
32936     GMapContext: function() 
32937     {
32938         var position = new google.maps.LatLng(this.latitude, this.longitude);
32939         
32940         var _map = new google.maps.Map(this.el.dom, {
32941             center: position,
32942             zoom: this.zoom,
32943             mapTypeId: this.mapTypeId,
32944             mapTypeControl: this.mapTypeControl,
32945             disableDoubleClickZoom: this.disableDoubleClickZoom,
32946             scrollwheel: this.scrollwheel,
32947             streetViewControl: this.streetViewControl,
32948             locationName: this.locationName,
32949             draggable: this.draggable,
32950             enableAutocomplete: this.enableAutocomplete,
32951             enableReverseGeocode: this.enableReverseGeocode
32952         });
32953         
32954         var _marker = new google.maps.Marker({
32955             position: position,
32956             map: _map,
32957             title: this.markerTitle,
32958             draggable: this.draggable
32959         });
32960         
32961         return {
32962             map: _map,
32963             marker: _marker,
32964             circle: null,
32965             location: position,
32966             radius: this.radius,
32967             locationName: this.locationName,
32968             addressComponents: {
32969                 formatted_address: null,
32970                 addressLine1: null,
32971                 addressLine2: null,
32972                 streetName: null,
32973                 streetNumber: null,
32974                 city: null,
32975                 district: null,
32976                 state: null,
32977                 stateOrProvince: null
32978             },
32979             settings: this,
32980             domContainer: this.el.dom,
32981             geodecoder: new google.maps.Geocoder()
32982         };
32983     },
32984     
32985     drawCircle: function(center, radius, options) 
32986     {
32987         if (this.gMapContext.circle != null) {
32988             this.gMapContext.circle.setMap(null);
32989         }
32990         if (radius > 0) {
32991             radius *= 1;
32992             options = Roo.apply({}, options, {
32993                 strokeColor: "#0000FF",
32994                 strokeOpacity: .35,
32995                 strokeWeight: 2,
32996                 fillColor: "#0000FF",
32997                 fillOpacity: .2
32998             });
32999             
33000             options.map = this.gMapContext.map;
33001             options.radius = radius;
33002             options.center = center;
33003             this.gMapContext.circle = new google.maps.Circle(options);
33004             return this.gMapContext.circle;
33005         }
33006         
33007         return null;
33008     },
33009     
33010     setPosition: function(location) 
33011     {
33012         this.gMapContext.location = location;
33013         this.gMapContext.marker.setPosition(location);
33014         this.gMapContext.map.panTo(location);
33015         this.drawCircle(location, this.gMapContext.radius, {});
33016         
33017         var _this = this;
33018         
33019         if (this.gMapContext.settings.enableReverseGeocode) {
33020             this.gMapContext.geodecoder.geocode({
33021                 latLng: this.gMapContext.location
33022             }, function(results, status) {
33023                 
33024                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
33025                     _this.gMapContext.locationName = results[0].formatted_address;
33026                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
33027                     
33028                     _this.fireEvent('positionchanged', this, location);
33029                 }
33030             });
33031             
33032             return;
33033         }
33034         
33035         this.fireEvent('positionchanged', this, location);
33036     },
33037     
33038     resize: function()
33039     {
33040         google.maps.event.trigger(this.gMapContext.map, "resize");
33041         
33042         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
33043         
33044         this.fireEvent('resize', this);
33045     },
33046     
33047     setPositionByLatLng: function(latitude, longitude)
33048     {
33049         this.setPosition(new google.maps.LatLng(latitude, longitude));
33050     },
33051     
33052     getCurrentPosition: function() 
33053     {
33054         return {
33055             latitude: this.gMapContext.location.lat(),
33056             longitude: this.gMapContext.location.lng()
33057         };
33058     },
33059     
33060     getAddressName: function() 
33061     {
33062         return this.gMapContext.locationName;
33063     },
33064     
33065     getAddressComponents: function() 
33066     {
33067         return this.gMapContext.addressComponents;
33068     },
33069     
33070     address_component_from_google_geocode: function(address_components) 
33071     {
33072         var result = {};
33073         
33074         for (var i = 0; i < address_components.length; i++) {
33075             var component = address_components[i];
33076             if (component.types.indexOf("postal_code") >= 0) {
33077                 result.postalCode = component.short_name;
33078             } else if (component.types.indexOf("street_number") >= 0) {
33079                 result.streetNumber = component.short_name;
33080             } else if (component.types.indexOf("route") >= 0) {
33081                 result.streetName = component.short_name;
33082             } else if (component.types.indexOf("neighborhood") >= 0) {
33083                 result.city = component.short_name;
33084             } else if (component.types.indexOf("locality") >= 0) {
33085                 result.city = component.short_name;
33086             } else if (component.types.indexOf("sublocality") >= 0) {
33087                 result.district = component.short_name;
33088             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
33089                 result.stateOrProvince = component.short_name;
33090             } else if (component.types.indexOf("country") >= 0) {
33091                 result.country = component.short_name;
33092             }
33093         }
33094         
33095         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
33096         result.addressLine2 = "";
33097         return result;
33098     },
33099     
33100     setZoomLevel: function(zoom)
33101     {
33102         this.gMapContext.map.setZoom(zoom);
33103     },
33104     
33105     show: function()
33106     {
33107         if(!this.el){
33108             return;
33109         }
33110         
33111         this.el.show();
33112         
33113         this.resize();
33114         
33115         this.fireEvent('show', this);
33116     },
33117     
33118     hide: function()
33119     {
33120         if(!this.el){
33121             return;
33122         }
33123         
33124         this.el.hide();
33125         
33126         this.fireEvent('hide', this);
33127     }
33128     
33129 });
33130
33131 Roo.apply(Roo.bootstrap.LocationPicker, {
33132     
33133     OverlayView : function(map, options)
33134     {
33135         options = options || {};
33136         
33137         this.setMap(map);
33138     }
33139     
33140     
33141 });/**
33142  * @class Roo.bootstrap.Alert
33143  * @extends Roo.bootstrap.Component
33144  * Bootstrap Alert class - shows an alert area box
33145  * eg
33146  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
33147   Enter a valid email address
33148 </div>
33149  * @licence LGPL
33150  * @cfg {String} title The title of alert
33151  * @cfg {String} html The content of alert
33152  * @cfg {String} weight (success|info|warning|danger) Weight of the message
33153  * @cfg {String} fa font-awesomeicon
33154  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
33155  * @cfg {Boolean} close true to show a x closer
33156  * 
33157  * 
33158  * @constructor
33159  * Create a new alert
33160  * @param {Object} config The config object
33161  */
33162
33163
33164 Roo.bootstrap.Alert = function(config){
33165     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
33166     
33167 };
33168
33169 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
33170     
33171     title: '',
33172     html: '',
33173     weight: false,
33174     fa: false,
33175     faicon: false, // BC
33176     close : false,
33177     
33178     
33179     getAutoCreate : function()
33180     {
33181         
33182         var cfg = {
33183             tag : 'div',
33184             cls : 'alert',
33185             cn : [
33186                 {
33187                     tag: 'button',
33188                     type :  "button",
33189                     cls: "close",
33190                     html : '×',
33191                     style : this.close ? '' : 'display:none'
33192                 },
33193                 {
33194                     tag : 'i',
33195                     cls : 'roo-alert-icon'
33196                     
33197                 },
33198                 {
33199                     tag : 'b',
33200                     cls : 'roo-alert-title',
33201                     html : this.title
33202                 },
33203                 {
33204                     tag : 'span',
33205                     cls : 'roo-alert-text',
33206                     html : this.html
33207                 }
33208             ]
33209         };
33210         
33211         if(this.faicon){
33212             cfg.cn[0].cls += ' fa ' + this.faicon;
33213         }
33214         if(this.fa){
33215             cfg.cn[0].cls += ' fa ' + this.fa;
33216         }
33217         
33218         if(this.weight){
33219             cfg.cls += ' alert-' + this.weight;
33220         }
33221         
33222         return cfg;
33223     },
33224     
33225     initEvents: function() 
33226     {
33227         this.el.setVisibilityMode(Roo.Element.DISPLAY);
33228         this.titleEl =  this.el.select('.roo-alert-title',true).first();
33229         this.iconEl = this.el.select('.roo-alert-icon',true).first();
33230         this.htmlEl = this.el.select('.roo-alert-text',true).first();
33231         if (this.seconds > 0) {
33232             this.hide.defer(this.seconds, this);
33233         }
33234     },
33235     /**
33236      * Set the Title Message HTML
33237      * @param {String} html
33238      */
33239     setTitle : function(str)
33240     {
33241         this.titleEl.dom.innerHTML = str;
33242     },
33243      
33244      /**
33245      * Set the Body Message HTML
33246      * @param {String} html
33247      */
33248     setHtml : function(str)
33249     {
33250         this.htmlEl.dom.innerHTML = str;
33251     },
33252     /**
33253      * Set the Weight of the alert
33254      * @param {String} (success|info|warning|danger) weight
33255      */
33256     
33257     setWeight : function(weight)
33258     {
33259         if(this.weight){
33260             this.el.removeClass('alert-' + this.weight);
33261         }
33262         
33263         this.weight = weight;
33264         
33265         this.el.addClass('alert-' + this.weight);
33266     },
33267       /**
33268      * Set the Icon of the alert
33269      * @param {String} see fontawsome names (name without the 'fa-' bit)
33270      */
33271     setIcon : function(icon)
33272     {
33273         if(this.faicon){
33274             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
33275         }
33276         
33277         this.faicon = icon;
33278         
33279         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
33280     },
33281     /**
33282      * Hide the Alert
33283      */
33284     hide: function() 
33285     {
33286         this.el.hide();   
33287     },
33288     /**
33289      * Show the Alert
33290      */
33291     show: function() 
33292     {  
33293         this.el.show();   
33294     }
33295     
33296 });
33297
33298  
33299 /*
33300 * Licence: LGPL
33301 */
33302
33303 /**
33304  * @class Roo.bootstrap.UploadCropbox
33305  * @extends Roo.bootstrap.Component
33306  * Bootstrap UploadCropbox class
33307  * @cfg {String} emptyText show when image has been loaded
33308  * @cfg {String} rotateNotify show when image too small to rotate
33309  * @cfg {Number} errorTimeout default 3000
33310  * @cfg {Number} minWidth default 300
33311  * @cfg {Number} minHeight default 300
33312  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
33313  * @cfg {Boolean} isDocument (true|false) default false
33314  * @cfg {String} url action url
33315  * @cfg {String} paramName default 'imageUpload'
33316  * @cfg {String} method default POST
33317  * @cfg {Boolean} loadMask (true|false) default true
33318  * @cfg {Boolean} loadingText default 'Loading...'
33319  * 
33320  * @constructor
33321  * Create a new UploadCropbox
33322  * @param {Object} config The config object
33323  */
33324
33325 Roo.bootstrap.UploadCropbox = function(config){
33326     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
33327     
33328     this.addEvents({
33329         /**
33330          * @event beforeselectfile
33331          * Fire before select file
33332          * @param {Roo.bootstrap.UploadCropbox} this
33333          */
33334         "beforeselectfile" : true,
33335         /**
33336          * @event initial
33337          * Fire after initEvent
33338          * @param {Roo.bootstrap.UploadCropbox} this
33339          */
33340         "initial" : true,
33341         /**
33342          * @event crop
33343          * Fire after initEvent
33344          * @param {Roo.bootstrap.UploadCropbox} this
33345          * @param {String} data
33346          */
33347         "crop" : true,
33348         /**
33349          * @event prepare
33350          * Fire when preparing the file data
33351          * @param {Roo.bootstrap.UploadCropbox} this
33352          * @param {Object} file
33353          */
33354         "prepare" : true,
33355         /**
33356          * @event exception
33357          * Fire when get exception
33358          * @param {Roo.bootstrap.UploadCropbox} this
33359          * @param {XMLHttpRequest} xhr
33360          */
33361         "exception" : true,
33362         /**
33363          * @event beforeloadcanvas
33364          * Fire before load the canvas
33365          * @param {Roo.bootstrap.UploadCropbox} this
33366          * @param {String} src
33367          */
33368         "beforeloadcanvas" : true,
33369         /**
33370          * @event trash
33371          * Fire when trash image
33372          * @param {Roo.bootstrap.UploadCropbox} this
33373          */
33374         "trash" : true,
33375         /**
33376          * @event download
33377          * Fire when download the image
33378          * @param {Roo.bootstrap.UploadCropbox} this
33379          */
33380         "download" : true,
33381         /**
33382          * @event footerbuttonclick
33383          * Fire when footerbuttonclick
33384          * @param {Roo.bootstrap.UploadCropbox} this
33385          * @param {String} type
33386          */
33387         "footerbuttonclick" : true,
33388         /**
33389          * @event resize
33390          * Fire when resize
33391          * @param {Roo.bootstrap.UploadCropbox} this
33392          */
33393         "resize" : true,
33394         /**
33395          * @event rotate
33396          * Fire when rotate the image
33397          * @param {Roo.bootstrap.UploadCropbox} this
33398          * @param {String} pos
33399          */
33400         "rotate" : true,
33401         /**
33402          * @event inspect
33403          * Fire when inspect the file
33404          * @param {Roo.bootstrap.UploadCropbox} this
33405          * @param {Object} file
33406          */
33407         "inspect" : true,
33408         /**
33409          * @event upload
33410          * Fire when xhr upload the file
33411          * @param {Roo.bootstrap.UploadCropbox} this
33412          * @param {Object} data
33413          */
33414         "upload" : true,
33415         /**
33416          * @event arrange
33417          * Fire when arrange the file data
33418          * @param {Roo.bootstrap.UploadCropbox} this
33419          * @param {Object} formData
33420          */
33421         "arrange" : true
33422     });
33423     
33424     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
33425 };
33426
33427 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
33428     
33429     emptyText : 'Click to upload image',
33430     rotateNotify : 'Image is too small to rotate',
33431     errorTimeout : 3000,
33432     scale : 0,
33433     baseScale : 1,
33434     rotate : 0,
33435     dragable : false,
33436     pinching : false,
33437     mouseX : 0,
33438     mouseY : 0,
33439     cropData : false,
33440     minWidth : 300,
33441     minHeight : 300,
33442     file : false,
33443     exif : {},
33444     baseRotate : 1,
33445     cropType : 'image/jpeg',
33446     buttons : false,
33447     canvasLoaded : false,
33448     isDocument : false,
33449     method : 'POST',
33450     paramName : 'imageUpload',
33451     loadMask : true,
33452     loadingText : 'Loading...',
33453     maskEl : false,
33454     
33455     getAutoCreate : function()
33456     {
33457         var cfg = {
33458             tag : 'div',
33459             cls : 'roo-upload-cropbox',
33460             cn : [
33461                 {
33462                     tag : 'input',
33463                     cls : 'roo-upload-cropbox-selector',
33464                     type : 'file'
33465                 },
33466                 {
33467                     tag : 'div',
33468                     cls : 'roo-upload-cropbox-body',
33469                     style : 'cursor:pointer',
33470                     cn : [
33471                         {
33472                             tag : 'div',
33473                             cls : 'roo-upload-cropbox-preview'
33474                         },
33475                         {
33476                             tag : 'div',
33477                             cls : 'roo-upload-cropbox-thumb'
33478                         },
33479                         {
33480                             tag : 'div',
33481                             cls : 'roo-upload-cropbox-empty-notify',
33482                             html : this.emptyText
33483                         },
33484                         {
33485                             tag : 'div',
33486                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
33487                             html : this.rotateNotify
33488                         }
33489                     ]
33490                 },
33491                 {
33492                     tag : 'div',
33493                     cls : 'roo-upload-cropbox-footer',
33494                     cn : {
33495                         tag : 'div',
33496                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
33497                         cn : []
33498                     }
33499                 }
33500             ]
33501         };
33502         
33503         return cfg;
33504     },
33505     
33506     onRender : function(ct, position)
33507     {
33508         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
33509         
33510         if (this.buttons.length) {
33511             
33512             Roo.each(this.buttons, function(bb) {
33513                 
33514                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
33515                 
33516                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
33517                 
33518             }, this);
33519         }
33520         
33521         if(this.loadMask){
33522             this.maskEl = this.el;
33523         }
33524     },
33525     
33526     initEvents : function()
33527     {
33528         this.urlAPI = (window.createObjectURL && window) || 
33529                                 (window.URL && URL.revokeObjectURL && URL) || 
33530                                 (window.webkitURL && webkitURL);
33531                         
33532         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
33533         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33534         
33535         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
33536         this.selectorEl.hide();
33537         
33538         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
33539         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33540         
33541         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
33542         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33543         this.thumbEl.hide();
33544         
33545         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
33546         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33547         
33548         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
33549         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33550         this.errorEl.hide();
33551         
33552         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
33553         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33554         this.footerEl.hide();
33555         
33556         this.setThumbBoxSize();
33557         
33558         this.bind();
33559         
33560         this.resize();
33561         
33562         this.fireEvent('initial', this);
33563     },
33564
33565     bind : function()
33566     {
33567         var _this = this;
33568         
33569         window.addEventListener("resize", function() { _this.resize(); } );
33570         
33571         this.bodyEl.on('click', this.beforeSelectFile, this);
33572         
33573         if(Roo.isTouch){
33574             this.bodyEl.on('touchstart', this.onTouchStart, this);
33575             this.bodyEl.on('touchmove', this.onTouchMove, this);
33576             this.bodyEl.on('touchend', this.onTouchEnd, this);
33577         }
33578         
33579         if(!Roo.isTouch){
33580             this.bodyEl.on('mousedown', this.onMouseDown, this);
33581             this.bodyEl.on('mousemove', this.onMouseMove, this);
33582             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
33583             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
33584             Roo.get(document).on('mouseup', this.onMouseUp, this);
33585         }
33586         
33587         this.selectorEl.on('change', this.onFileSelected, this);
33588     },
33589     
33590     reset : function()
33591     {    
33592         this.scale = 0;
33593         this.baseScale = 1;
33594         this.rotate = 0;
33595         this.baseRotate = 1;
33596         this.dragable = false;
33597         this.pinching = false;
33598         this.mouseX = 0;
33599         this.mouseY = 0;
33600         this.cropData = false;
33601         this.notifyEl.dom.innerHTML = this.emptyText;
33602         
33603         this.selectorEl.dom.value = '';
33604         
33605     },
33606     
33607     resize : function()
33608     {
33609         if(this.fireEvent('resize', this) != false){
33610             this.setThumbBoxPosition();
33611             this.setCanvasPosition();
33612         }
33613     },
33614     
33615     onFooterButtonClick : function(e, el, o, type)
33616     {
33617         switch (type) {
33618             case 'rotate-left' :
33619                 this.onRotateLeft(e);
33620                 break;
33621             case 'rotate-right' :
33622                 this.onRotateRight(e);
33623                 break;
33624             case 'picture' :
33625                 this.beforeSelectFile(e);
33626                 break;
33627             case 'trash' :
33628                 this.trash(e);
33629                 break;
33630             case 'crop' :
33631                 this.crop(e);
33632                 break;
33633             case 'download' :
33634                 this.download(e);
33635                 break;
33636             default :
33637                 break;
33638         }
33639         
33640         this.fireEvent('footerbuttonclick', this, type);
33641     },
33642     
33643     beforeSelectFile : function(e)
33644     {
33645         e.preventDefault();
33646         
33647         if(this.fireEvent('beforeselectfile', this) != false){
33648             this.selectorEl.dom.click();
33649         }
33650     },
33651     
33652     onFileSelected : function(e)
33653     {
33654         e.preventDefault();
33655         
33656         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
33657             return;
33658         }
33659         
33660         var file = this.selectorEl.dom.files[0];
33661         
33662         if(this.fireEvent('inspect', this, file) != false){
33663             this.prepare(file);
33664         }
33665         
33666     },
33667     
33668     trash : function(e)
33669     {
33670         this.fireEvent('trash', this);
33671     },
33672     
33673     download : function(e)
33674     {
33675         this.fireEvent('download', this);
33676     },
33677     
33678     loadCanvas : function(src)
33679     {   
33680         if(this.fireEvent('beforeloadcanvas', this, src) != false){
33681             
33682             this.reset();
33683             
33684             this.imageEl = document.createElement('img');
33685             
33686             var _this = this;
33687             
33688             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
33689             
33690             this.imageEl.src = src;
33691         }
33692     },
33693     
33694     onLoadCanvas : function()
33695     {   
33696         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
33697         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
33698         
33699         this.bodyEl.un('click', this.beforeSelectFile, this);
33700         
33701         this.notifyEl.hide();
33702         this.thumbEl.show();
33703         this.footerEl.show();
33704         
33705         this.baseRotateLevel();
33706         
33707         if(this.isDocument){
33708             this.setThumbBoxSize();
33709         }
33710         
33711         this.setThumbBoxPosition();
33712         
33713         this.baseScaleLevel();
33714         
33715         this.draw();
33716         
33717         this.resize();
33718         
33719         this.canvasLoaded = true;
33720         
33721         if(this.loadMask){
33722             this.maskEl.unmask();
33723         }
33724         
33725     },
33726     
33727     setCanvasPosition : function()
33728     {   
33729         if(!this.canvasEl){
33730             return;
33731         }
33732         
33733         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
33734         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
33735         
33736         this.previewEl.setLeft(pw);
33737         this.previewEl.setTop(ph);
33738         
33739     },
33740     
33741     onMouseDown : function(e)
33742     {   
33743         e.stopEvent();
33744         
33745         this.dragable = true;
33746         this.pinching = false;
33747         
33748         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
33749             this.dragable = false;
33750             return;
33751         }
33752         
33753         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33754         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33755         
33756     },
33757     
33758     onMouseMove : function(e)
33759     {   
33760         e.stopEvent();
33761         
33762         if(!this.canvasLoaded){
33763             return;
33764         }
33765         
33766         if (!this.dragable){
33767             return;
33768         }
33769         
33770         var minX = Math.ceil(this.thumbEl.getLeft(true));
33771         var minY = Math.ceil(this.thumbEl.getTop(true));
33772         
33773         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
33774         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
33775         
33776         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33777         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33778         
33779         x = x - this.mouseX;
33780         y = y - this.mouseY;
33781         
33782         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
33783         var bgY = Math.ceil(y + this.previewEl.getTop(true));
33784         
33785         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
33786         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
33787         
33788         this.previewEl.setLeft(bgX);
33789         this.previewEl.setTop(bgY);
33790         
33791         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33792         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33793     },
33794     
33795     onMouseUp : function(e)
33796     {   
33797         e.stopEvent();
33798         
33799         this.dragable = false;
33800     },
33801     
33802     onMouseWheel : function(e)
33803     {   
33804         e.stopEvent();
33805         
33806         this.startScale = this.scale;
33807         
33808         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
33809         
33810         if(!this.zoomable()){
33811             this.scale = this.startScale;
33812             return;
33813         }
33814         
33815         this.draw();
33816         
33817         return;
33818     },
33819     
33820     zoomable : function()
33821     {
33822         var minScale = this.thumbEl.getWidth() / this.minWidth;
33823         
33824         if(this.minWidth < this.minHeight){
33825             minScale = this.thumbEl.getHeight() / this.minHeight;
33826         }
33827         
33828         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
33829         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
33830         
33831         if(
33832                 this.isDocument &&
33833                 (this.rotate == 0 || this.rotate == 180) && 
33834                 (
33835                     width > this.imageEl.OriginWidth || 
33836                     height > this.imageEl.OriginHeight ||
33837                     (width < this.minWidth && height < this.minHeight)
33838                 )
33839         ){
33840             return false;
33841         }
33842         
33843         if(
33844                 this.isDocument &&
33845                 (this.rotate == 90 || this.rotate == 270) && 
33846                 (
33847                     width > this.imageEl.OriginWidth || 
33848                     height > this.imageEl.OriginHeight ||
33849                     (width < this.minHeight && height < this.minWidth)
33850                 )
33851         ){
33852             return false;
33853         }
33854         
33855         if(
33856                 !this.isDocument &&
33857                 (this.rotate == 0 || this.rotate == 180) && 
33858                 (
33859                     width < this.minWidth || 
33860                     width > this.imageEl.OriginWidth || 
33861                     height < this.minHeight || 
33862                     height > this.imageEl.OriginHeight
33863                 )
33864         ){
33865             return false;
33866         }
33867         
33868         if(
33869                 !this.isDocument &&
33870                 (this.rotate == 90 || this.rotate == 270) && 
33871                 (
33872                     width < this.minHeight || 
33873                     width > this.imageEl.OriginWidth || 
33874                     height < this.minWidth || 
33875                     height > this.imageEl.OriginHeight
33876                 )
33877         ){
33878             return false;
33879         }
33880         
33881         return true;
33882         
33883     },
33884     
33885     onRotateLeft : function(e)
33886     {   
33887         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
33888             
33889             var minScale = this.thumbEl.getWidth() / this.minWidth;
33890             
33891             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
33892             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
33893             
33894             this.startScale = this.scale;
33895             
33896             while (this.getScaleLevel() < minScale){
33897             
33898                 this.scale = this.scale + 1;
33899                 
33900                 if(!this.zoomable()){
33901                     break;
33902                 }
33903                 
33904                 if(
33905                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
33906                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
33907                 ){
33908                     continue;
33909                 }
33910                 
33911                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
33912
33913                 this.draw();
33914                 
33915                 return;
33916             }
33917             
33918             this.scale = this.startScale;
33919             
33920             this.onRotateFail();
33921             
33922             return false;
33923         }
33924         
33925         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
33926
33927         if(this.isDocument){
33928             this.setThumbBoxSize();
33929             this.setThumbBoxPosition();
33930             this.setCanvasPosition();
33931         }
33932         
33933         this.draw();
33934         
33935         this.fireEvent('rotate', this, 'left');
33936         
33937     },
33938     
33939     onRotateRight : function(e)
33940     {
33941         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
33942             
33943             var minScale = this.thumbEl.getWidth() / this.minWidth;
33944         
33945             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
33946             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
33947             
33948             this.startScale = this.scale;
33949             
33950             while (this.getScaleLevel() < minScale){
33951             
33952                 this.scale = this.scale + 1;
33953                 
33954                 if(!this.zoomable()){
33955                     break;
33956                 }
33957                 
33958                 if(
33959                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
33960                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
33961                 ){
33962                     continue;
33963                 }
33964                 
33965                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
33966
33967                 this.draw();
33968                 
33969                 return;
33970             }
33971             
33972             this.scale = this.startScale;
33973             
33974             this.onRotateFail();
33975             
33976             return false;
33977         }
33978         
33979         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
33980
33981         if(this.isDocument){
33982             this.setThumbBoxSize();
33983             this.setThumbBoxPosition();
33984             this.setCanvasPosition();
33985         }
33986         
33987         this.draw();
33988         
33989         this.fireEvent('rotate', this, 'right');
33990     },
33991     
33992     onRotateFail : function()
33993     {
33994         this.errorEl.show(true);
33995         
33996         var _this = this;
33997         
33998         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
33999     },
34000     
34001     draw : function()
34002     {
34003         this.previewEl.dom.innerHTML = '';
34004         
34005         var canvasEl = document.createElement("canvas");
34006         
34007         var contextEl = canvasEl.getContext("2d");
34008         
34009         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34010         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34011         var center = this.imageEl.OriginWidth / 2;
34012         
34013         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
34014             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34015             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34016             center = this.imageEl.OriginHeight / 2;
34017         }
34018         
34019         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
34020         
34021         contextEl.translate(center, center);
34022         contextEl.rotate(this.rotate * Math.PI / 180);
34023
34024         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34025         
34026         this.canvasEl = document.createElement("canvas");
34027         
34028         this.contextEl = this.canvasEl.getContext("2d");
34029         
34030         switch (this.rotate) {
34031             case 0 :
34032                 
34033                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34034                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34035                 
34036                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34037                 
34038                 break;
34039             case 90 : 
34040                 
34041                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34042                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34043                 
34044                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34045                     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);
34046                     break;
34047                 }
34048                 
34049                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34050                 
34051                 break;
34052             case 180 :
34053                 
34054                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34055                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34056                 
34057                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34058                     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);
34059                     break;
34060                 }
34061                 
34062                 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);
34063                 
34064                 break;
34065             case 270 :
34066                 
34067                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34068                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34069         
34070                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34071                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34072                     break;
34073                 }
34074                 
34075                 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);
34076                 
34077                 break;
34078             default : 
34079                 break;
34080         }
34081         
34082         this.previewEl.appendChild(this.canvasEl);
34083         
34084         this.setCanvasPosition();
34085     },
34086     
34087     crop : function()
34088     {
34089         if(!this.canvasLoaded){
34090             return;
34091         }
34092         
34093         var imageCanvas = document.createElement("canvas");
34094         
34095         var imageContext = imageCanvas.getContext("2d");
34096         
34097         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34098         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34099         
34100         var center = imageCanvas.width / 2;
34101         
34102         imageContext.translate(center, center);
34103         
34104         imageContext.rotate(this.rotate * Math.PI / 180);
34105         
34106         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34107         
34108         var canvas = document.createElement("canvas");
34109         
34110         var context = canvas.getContext("2d");
34111                 
34112         canvas.width = this.minWidth;
34113         canvas.height = this.minHeight;
34114
34115         switch (this.rotate) {
34116             case 0 :
34117                 
34118                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34119                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34120                 
34121                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34122                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34123                 
34124                 var targetWidth = this.minWidth - 2 * x;
34125                 var targetHeight = this.minHeight - 2 * y;
34126                 
34127                 var scale = 1;
34128                 
34129                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34130                     scale = targetWidth / width;
34131                 }
34132                 
34133                 if(x > 0 && y == 0){
34134                     scale = targetHeight / height;
34135                 }
34136                 
34137                 if(x > 0 && y > 0){
34138                     scale = targetWidth / width;
34139                     
34140                     if(width < height){
34141                         scale = targetHeight / height;
34142                     }
34143                 }
34144                 
34145                 context.scale(scale, scale);
34146                 
34147                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34148                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34149
34150                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34151                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34152
34153                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34154                 
34155                 break;
34156             case 90 : 
34157                 
34158                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34159                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34160                 
34161                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34162                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34163                 
34164                 var targetWidth = this.minWidth - 2 * x;
34165                 var targetHeight = this.minHeight - 2 * y;
34166                 
34167                 var scale = 1;
34168                 
34169                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34170                     scale = targetWidth / width;
34171                 }
34172                 
34173                 if(x > 0 && y == 0){
34174                     scale = targetHeight / height;
34175                 }
34176                 
34177                 if(x > 0 && y > 0){
34178                     scale = targetWidth / width;
34179                     
34180                     if(width < height){
34181                         scale = targetHeight / height;
34182                     }
34183                 }
34184                 
34185                 context.scale(scale, scale);
34186                 
34187                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34188                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34189
34190                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34191                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34192                 
34193                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34194                 
34195                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34196                 
34197                 break;
34198             case 180 :
34199                 
34200                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34201                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34202                 
34203                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34204                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34205                 
34206                 var targetWidth = this.minWidth - 2 * x;
34207                 var targetHeight = this.minHeight - 2 * y;
34208                 
34209                 var scale = 1;
34210                 
34211                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34212                     scale = targetWidth / width;
34213                 }
34214                 
34215                 if(x > 0 && y == 0){
34216                     scale = targetHeight / height;
34217                 }
34218                 
34219                 if(x > 0 && y > 0){
34220                     scale = targetWidth / width;
34221                     
34222                     if(width < height){
34223                         scale = targetHeight / height;
34224                     }
34225                 }
34226                 
34227                 context.scale(scale, scale);
34228                 
34229                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34230                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34231
34232                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34233                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34234
34235                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34236                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34237                 
34238                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34239                 
34240                 break;
34241             case 270 :
34242                 
34243                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34244                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34245                 
34246                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34247                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34248                 
34249                 var targetWidth = this.minWidth - 2 * x;
34250                 var targetHeight = this.minHeight - 2 * y;
34251                 
34252                 var scale = 1;
34253                 
34254                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34255                     scale = targetWidth / width;
34256                 }
34257                 
34258                 if(x > 0 && y == 0){
34259                     scale = targetHeight / height;
34260                 }
34261                 
34262                 if(x > 0 && y > 0){
34263                     scale = targetWidth / width;
34264                     
34265                     if(width < height){
34266                         scale = targetHeight / height;
34267                     }
34268                 }
34269                 
34270                 context.scale(scale, scale);
34271                 
34272                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34273                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34274
34275                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34276                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34277                 
34278                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34279                 
34280                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34281                 
34282                 break;
34283             default : 
34284                 break;
34285         }
34286         
34287         this.cropData = canvas.toDataURL(this.cropType);
34288         
34289         if(this.fireEvent('crop', this, this.cropData) !== false){
34290             this.process(this.file, this.cropData);
34291         }
34292         
34293         return;
34294         
34295     },
34296     
34297     setThumbBoxSize : function()
34298     {
34299         var width, height;
34300         
34301         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
34302             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
34303             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
34304             
34305             this.minWidth = width;
34306             this.minHeight = height;
34307             
34308             if(this.rotate == 90 || this.rotate == 270){
34309                 this.minWidth = height;
34310                 this.minHeight = width;
34311             }
34312         }
34313         
34314         height = 300;
34315         width = Math.ceil(this.minWidth * height / this.minHeight);
34316         
34317         if(this.minWidth > this.minHeight){
34318             width = 300;
34319             height = Math.ceil(this.minHeight * width / this.minWidth);
34320         }
34321         
34322         this.thumbEl.setStyle({
34323             width : width + 'px',
34324             height : height + 'px'
34325         });
34326
34327         return;
34328             
34329     },
34330     
34331     setThumbBoxPosition : function()
34332     {
34333         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
34334         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
34335         
34336         this.thumbEl.setLeft(x);
34337         this.thumbEl.setTop(y);
34338         
34339     },
34340     
34341     baseRotateLevel : function()
34342     {
34343         this.baseRotate = 1;
34344         
34345         if(
34346                 typeof(this.exif) != 'undefined' &&
34347                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
34348                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
34349         ){
34350             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
34351         }
34352         
34353         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
34354         
34355     },
34356     
34357     baseScaleLevel : function()
34358     {
34359         var width, height;
34360         
34361         if(this.isDocument){
34362             
34363             if(this.baseRotate == 6 || this.baseRotate == 8){
34364             
34365                 height = this.thumbEl.getHeight();
34366                 this.baseScale = height / this.imageEl.OriginWidth;
34367
34368                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
34369                     width = this.thumbEl.getWidth();
34370                     this.baseScale = width / this.imageEl.OriginHeight;
34371                 }
34372
34373                 return;
34374             }
34375
34376             height = this.thumbEl.getHeight();
34377             this.baseScale = height / this.imageEl.OriginHeight;
34378
34379             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
34380                 width = this.thumbEl.getWidth();
34381                 this.baseScale = width / this.imageEl.OriginWidth;
34382             }
34383
34384             return;
34385         }
34386         
34387         if(this.baseRotate == 6 || this.baseRotate == 8){
34388             
34389             width = this.thumbEl.getHeight();
34390             this.baseScale = width / this.imageEl.OriginHeight;
34391             
34392             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
34393                 height = this.thumbEl.getWidth();
34394                 this.baseScale = height / this.imageEl.OriginHeight;
34395             }
34396             
34397             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34398                 height = this.thumbEl.getWidth();
34399                 this.baseScale = height / this.imageEl.OriginHeight;
34400                 
34401                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
34402                     width = this.thumbEl.getHeight();
34403                     this.baseScale = width / this.imageEl.OriginWidth;
34404                 }
34405             }
34406             
34407             return;
34408         }
34409         
34410         width = this.thumbEl.getWidth();
34411         this.baseScale = width / this.imageEl.OriginWidth;
34412         
34413         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
34414             height = this.thumbEl.getHeight();
34415             this.baseScale = height / this.imageEl.OriginHeight;
34416         }
34417         
34418         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34419             
34420             height = this.thumbEl.getHeight();
34421             this.baseScale = height / this.imageEl.OriginHeight;
34422             
34423             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
34424                 width = this.thumbEl.getWidth();
34425                 this.baseScale = width / this.imageEl.OriginWidth;
34426             }
34427             
34428         }
34429         
34430         return;
34431     },
34432     
34433     getScaleLevel : function()
34434     {
34435         return this.baseScale * Math.pow(1.1, this.scale);
34436     },
34437     
34438     onTouchStart : function(e)
34439     {
34440         if(!this.canvasLoaded){
34441             this.beforeSelectFile(e);
34442             return;
34443         }
34444         
34445         var touches = e.browserEvent.touches;
34446         
34447         if(!touches){
34448             return;
34449         }
34450         
34451         if(touches.length == 1){
34452             this.onMouseDown(e);
34453             return;
34454         }
34455         
34456         if(touches.length != 2){
34457             return;
34458         }
34459         
34460         var coords = [];
34461         
34462         for(var i = 0, finger; finger = touches[i]; i++){
34463             coords.push(finger.pageX, finger.pageY);
34464         }
34465         
34466         var x = Math.pow(coords[0] - coords[2], 2);
34467         var y = Math.pow(coords[1] - coords[3], 2);
34468         
34469         this.startDistance = Math.sqrt(x + y);
34470         
34471         this.startScale = this.scale;
34472         
34473         this.pinching = true;
34474         this.dragable = false;
34475         
34476     },
34477     
34478     onTouchMove : function(e)
34479     {
34480         if(!this.pinching && !this.dragable){
34481             return;
34482         }
34483         
34484         var touches = e.browserEvent.touches;
34485         
34486         if(!touches){
34487             return;
34488         }
34489         
34490         if(this.dragable){
34491             this.onMouseMove(e);
34492             return;
34493         }
34494         
34495         var coords = [];
34496         
34497         for(var i = 0, finger; finger = touches[i]; i++){
34498             coords.push(finger.pageX, finger.pageY);
34499         }
34500         
34501         var x = Math.pow(coords[0] - coords[2], 2);
34502         var y = Math.pow(coords[1] - coords[3], 2);
34503         
34504         this.endDistance = Math.sqrt(x + y);
34505         
34506         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
34507         
34508         if(!this.zoomable()){
34509             this.scale = this.startScale;
34510             return;
34511         }
34512         
34513         this.draw();
34514         
34515     },
34516     
34517     onTouchEnd : function(e)
34518     {
34519         this.pinching = false;
34520         this.dragable = false;
34521         
34522     },
34523     
34524     process : function(file, crop)
34525     {
34526         if(this.loadMask){
34527             this.maskEl.mask(this.loadingText);
34528         }
34529         
34530         this.xhr = new XMLHttpRequest();
34531         
34532         file.xhr = this.xhr;
34533
34534         this.xhr.open(this.method, this.url, true);
34535         
34536         var headers = {
34537             "Accept": "application/json",
34538             "Cache-Control": "no-cache",
34539             "X-Requested-With": "XMLHttpRequest"
34540         };
34541         
34542         for (var headerName in headers) {
34543             var headerValue = headers[headerName];
34544             if (headerValue) {
34545                 this.xhr.setRequestHeader(headerName, headerValue);
34546             }
34547         }
34548         
34549         var _this = this;
34550         
34551         this.xhr.onload = function()
34552         {
34553             _this.xhrOnLoad(_this.xhr);
34554         }
34555         
34556         this.xhr.onerror = function()
34557         {
34558             _this.xhrOnError(_this.xhr);
34559         }
34560         
34561         var formData = new FormData();
34562
34563         formData.append('returnHTML', 'NO');
34564         
34565         if(crop){
34566             formData.append('crop', crop);
34567         }
34568         
34569         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
34570             formData.append(this.paramName, file, file.name);
34571         }
34572         
34573         if(typeof(file.filename) != 'undefined'){
34574             formData.append('filename', file.filename);
34575         }
34576         
34577         if(typeof(file.mimetype) != 'undefined'){
34578             formData.append('mimetype', file.mimetype);
34579         }
34580         
34581         if(this.fireEvent('arrange', this, formData) != false){
34582             this.xhr.send(formData);
34583         };
34584     },
34585     
34586     xhrOnLoad : function(xhr)
34587     {
34588         if(this.loadMask){
34589             this.maskEl.unmask();
34590         }
34591         
34592         if (xhr.readyState !== 4) {
34593             this.fireEvent('exception', this, xhr);
34594             return;
34595         }
34596
34597         var response = Roo.decode(xhr.responseText);
34598         
34599         if(!response.success){
34600             this.fireEvent('exception', this, xhr);
34601             return;
34602         }
34603         
34604         var response = Roo.decode(xhr.responseText);
34605         
34606         this.fireEvent('upload', this, response);
34607         
34608     },
34609     
34610     xhrOnError : function()
34611     {
34612         if(this.loadMask){
34613             this.maskEl.unmask();
34614         }
34615         
34616         Roo.log('xhr on error');
34617         
34618         var response = Roo.decode(xhr.responseText);
34619           
34620         Roo.log(response);
34621         
34622     },
34623     
34624     prepare : function(file)
34625     {   
34626         if(this.loadMask){
34627             this.maskEl.mask(this.loadingText);
34628         }
34629         
34630         this.file = false;
34631         this.exif = {};
34632         
34633         if(typeof(file) === 'string'){
34634             this.loadCanvas(file);
34635             return;
34636         }
34637         
34638         if(!file || !this.urlAPI){
34639             return;
34640         }
34641         
34642         this.file = file;
34643         this.cropType = file.type;
34644         
34645         var _this = this;
34646         
34647         if(this.fireEvent('prepare', this, this.file) != false){
34648             
34649             var reader = new FileReader();
34650             
34651             reader.onload = function (e) {
34652                 if (e.target.error) {
34653                     Roo.log(e.target.error);
34654                     return;
34655                 }
34656                 
34657                 var buffer = e.target.result,
34658                     dataView = new DataView(buffer),
34659                     offset = 2,
34660                     maxOffset = dataView.byteLength - 4,
34661                     markerBytes,
34662                     markerLength;
34663                 
34664                 if (dataView.getUint16(0) === 0xffd8) {
34665                     while (offset < maxOffset) {
34666                         markerBytes = dataView.getUint16(offset);
34667                         
34668                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
34669                             markerLength = dataView.getUint16(offset + 2) + 2;
34670                             if (offset + markerLength > dataView.byteLength) {
34671                                 Roo.log('Invalid meta data: Invalid segment size.');
34672                                 break;
34673                             }
34674                             
34675                             if(markerBytes == 0xffe1){
34676                                 _this.parseExifData(
34677                                     dataView,
34678                                     offset,
34679                                     markerLength
34680                                 );
34681                             }
34682                             
34683                             offset += markerLength;
34684                             
34685                             continue;
34686                         }
34687                         
34688                         break;
34689                     }
34690                     
34691                 }
34692                 
34693                 var url = _this.urlAPI.createObjectURL(_this.file);
34694                 
34695                 _this.loadCanvas(url);
34696                 
34697                 return;
34698             }
34699             
34700             reader.readAsArrayBuffer(this.file);
34701             
34702         }
34703         
34704     },
34705     
34706     parseExifData : function(dataView, offset, length)
34707     {
34708         var tiffOffset = offset + 10,
34709             littleEndian,
34710             dirOffset;
34711     
34712         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34713             // No Exif data, might be XMP data instead
34714             return;
34715         }
34716         
34717         // Check for the ASCII code for "Exif" (0x45786966):
34718         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34719             // No Exif data, might be XMP data instead
34720             return;
34721         }
34722         if (tiffOffset + 8 > dataView.byteLength) {
34723             Roo.log('Invalid Exif data: Invalid segment size.');
34724             return;
34725         }
34726         // Check for the two null bytes:
34727         if (dataView.getUint16(offset + 8) !== 0x0000) {
34728             Roo.log('Invalid Exif data: Missing byte alignment offset.');
34729             return;
34730         }
34731         // Check the byte alignment:
34732         switch (dataView.getUint16(tiffOffset)) {
34733         case 0x4949:
34734             littleEndian = true;
34735             break;
34736         case 0x4D4D:
34737             littleEndian = false;
34738             break;
34739         default:
34740             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
34741             return;
34742         }
34743         // Check for the TIFF tag marker (0x002A):
34744         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
34745             Roo.log('Invalid Exif data: Missing TIFF marker.');
34746             return;
34747         }
34748         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
34749         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
34750         
34751         this.parseExifTags(
34752             dataView,
34753             tiffOffset,
34754             tiffOffset + dirOffset,
34755             littleEndian
34756         );
34757     },
34758     
34759     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
34760     {
34761         var tagsNumber,
34762             dirEndOffset,
34763             i;
34764         if (dirOffset + 6 > dataView.byteLength) {
34765             Roo.log('Invalid Exif data: Invalid directory offset.');
34766             return;
34767         }
34768         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
34769         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
34770         if (dirEndOffset + 4 > dataView.byteLength) {
34771             Roo.log('Invalid Exif data: Invalid directory size.');
34772             return;
34773         }
34774         for (i = 0; i < tagsNumber; i += 1) {
34775             this.parseExifTag(
34776                 dataView,
34777                 tiffOffset,
34778                 dirOffset + 2 + 12 * i, // tag offset
34779                 littleEndian
34780             );
34781         }
34782         // Return the offset to the next directory:
34783         return dataView.getUint32(dirEndOffset, littleEndian);
34784     },
34785     
34786     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
34787     {
34788         var tag = dataView.getUint16(offset, littleEndian);
34789         
34790         this.exif[tag] = this.getExifValue(
34791             dataView,
34792             tiffOffset,
34793             offset,
34794             dataView.getUint16(offset + 2, littleEndian), // tag type
34795             dataView.getUint32(offset + 4, littleEndian), // tag length
34796             littleEndian
34797         );
34798     },
34799     
34800     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
34801     {
34802         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
34803             tagSize,
34804             dataOffset,
34805             values,
34806             i,
34807             str,
34808             c;
34809     
34810         if (!tagType) {
34811             Roo.log('Invalid Exif data: Invalid tag type.');
34812             return;
34813         }
34814         
34815         tagSize = tagType.size * length;
34816         // Determine if the value is contained in the dataOffset bytes,
34817         // or if the value at the dataOffset is a pointer to the actual data:
34818         dataOffset = tagSize > 4 ?
34819                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
34820         if (dataOffset + tagSize > dataView.byteLength) {
34821             Roo.log('Invalid Exif data: Invalid data offset.');
34822             return;
34823         }
34824         if (length === 1) {
34825             return tagType.getValue(dataView, dataOffset, littleEndian);
34826         }
34827         values = [];
34828         for (i = 0; i < length; i += 1) {
34829             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
34830         }
34831         
34832         if (tagType.ascii) {
34833             str = '';
34834             // Concatenate the chars:
34835             for (i = 0; i < values.length; i += 1) {
34836                 c = values[i];
34837                 // Ignore the terminating NULL byte(s):
34838                 if (c === '\u0000') {
34839                     break;
34840                 }
34841                 str += c;
34842             }
34843             return str;
34844         }
34845         return values;
34846     }
34847     
34848 });
34849
34850 Roo.apply(Roo.bootstrap.UploadCropbox, {
34851     tags : {
34852         'Orientation': 0x0112
34853     },
34854     
34855     Orientation: {
34856             1: 0, //'top-left',
34857 //            2: 'top-right',
34858             3: 180, //'bottom-right',
34859 //            4: 'bottom-left',
34860 //            5: 'left-top',
34861             6: 90, //'right-top',
34862 //            7: 'right-bottom',
34863             8: 270 //'left-bottom'
34864     },
34865     
34866     exifTagTypes : {
34867         // byte, 8-bit unsigned int:
34868         1: {
34869             getValue: function (dataView, dataOffset) {
34870                 return dataView.getUint8(dataOffset);
34871             },
34872             size: 1
34873         },
34874         // ascii, 8-bit byte:
34875         2: {
34876             getValue: function (dataView, dataOffset) {
34877                 return String.fromCharCode(dataView.getUint8(dataOffset));
34878             },
34879             size: 1,
34880             ascii: true
34881         },
34882         // short, 16 bit int:
34883         3: {
34884             getValue: function (dataView, dataOffset, littleEndian) {
34885                 return dataView.getUint16(dataOffset, littleEndian);
34886             },
34887             size: 2
34888         },
34889         // long, 32 bit int:
34890         4: {
34891             getValue: function (dataView, dataOffset, littleEndian) {
34892                 return dataView.getUint32(dataOffset, littleEndian);
34893             },
34894             size: 4
34895         },
34896         // rational = two long values, first is numerator, second is denominator:
34897         5: {
34898             getValue: function (dataView, dataOffset, littleEndian) {
34899                 return dataView.getUint32(dataOffset, littleEndian) /
34900                     dataView.getUint32(dataOffset + 4, littleEndian);
34901             },
34902             size: 8
34903         },
34904         // slong, 32 bit signed int:
34905         9: {
34906             getValue: function (dataView, dataOffset, littleEndian) {
34907                 return dataView.getInt32(dataOffset, littleEndian);
34908             },
34909             size: 4
34910         },
34911         // srational, two slongs, first is numerator, second is denominator:
34912         10: {
34913             getValue: function (dataView, dataOffset, littleEndian) {
34914                 return dataView.getInt32(dataOffset, littleEndian) /
34915                     dataView.getInt32(dataOffset + 4, littleEndian);
34916             },
34917             size: 8
34918         }
34919     },
34920     
34921     footer : {
34922         STANDARD : [
34923             {
34924                 tag : 'div',
34925                 cls : 'btn-group roo-upload-cropbox-rotate-left',
34926                 action : 'rotate-left',
34927                 cn : [
34928                     {
34929                         tag : 'button',
34930                         cls : 'btn btn-default',
34931                         html : '<i class="fa fa-undo"></i>'
34932                     }
34933                 ]
34934             },
34935             {
34936                 tag : 'div',
34937                 cls : 'btn-group roo-upload-cropbox-picture',
34938                 action : 'picture',
34939                 cn : [
34940                     {
34941                         tag : 'button',
34942                         cls : 'btn btn-default',
34943                         html : '<i class="fa fa-picture-o"></i>'
34944                     }
34945                 ]
34946             },
34947             {
34948                 tag : 'div',
34949                 cls : 'btn-group roo-upload-cropbox-rotate-right',
34950                 action : 'rotate-right',
34951                 cn : [
34952                     {
34953                         tag : 'button',
34954                         cls : 'btn btn-default',
34955                         html : '<i class="fa fa-repeat"></i>'
34956                     }
34957                 ]
34958             }
34959         ],
34960         DOCUMENT : [
34961             {
34962                 tag : 'div',
34963                 cls : 'btn-group roo-upload-cropbox-rotate-left',
34964                 action : 'rotate-left',
34965                 cn : [
34966                     {
34967                         tag : 'button',
34968                         cls : 'btn btn-default',
34969                         html : '<i class="fa fa-undo"></i>'
34970                     }
34971                 ]
34972             },
34973             {
34974                 tag : 'div',
34975                 cls : 'btn-group roo-upload-cropbox-download',
34976                 action : 'download',
34977                 cn : [
34978                     {
34979                         tag : 'button',
34980                         cls : 'btn btn-default',
34981                         html : '<i class="fa fa-download"></i>'
34982                     }
34983                 ]
34984             },
34985             {
34986                 tag : 'div',
34987                 cls : 'btn-group roo-upload-cropbox-crop',
34988                 action : 'crop',
34989                 cn : [
34990                     {
34991                         tag : 'button',
34992                         cls : 'btn btn-default',
34993                         html : '<i class="fa fa-crop"></i>'
34994                     }
34995                 ]
34996             },
34997             {
34998                 tag : 'div',
34999                 cls : 'btn-group roo-upload-cropbox-trash',
35000                 action : 'trash',
35001                 cn : [
35002                     {
35003                         tag : 'button',
35004                         cls : 'btn btn-default',
35005                         html : '<i class="fa fa-trash"></i>'
35006                     }
35007                 ]
35008             },
35009             {
35010                 tag : 'div',
35011                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35012                 action : 'rotate-right',
35013                 cn : [
35014                     {
35015                         tag : 'button',
35016                         cls : 'btn btn-default',
35017                         html : '<i class="fa fa-repeat"></i>'
35018                     }
35019                 ]
35020             }
35021         ],
35022         ROTATOR : [
35023             {
35024                 tag : 'div',
35025                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35026                 action : 'rotate-left',
35027                 cn : [
35028                     {
35029                         tag : 'button',
35030                         cls : 'btn btn-default',
35031                         html : '<i class="fa fa-undo"></i>'
35032                     }
35033                 ]
35034             },
35035             {
35036                 tag : 'div',
35037                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35038                 action : 'rotate-right',
35039                 cn : [
35040                     {
35041                         tag : 'button',
35042                         cls : 'btn btn-default',
35043                         html : '<i class="fa fa-repeat"></i>'
35044                     }
35045                 ]
35046             }
35047         ]
35048     }
35049 });
35050
35051 /*
35052 * Licence: LGPL
35053 */
35054
35055 /**
35056  * @class Roo.bootstrap.DocumentManager
35057  * @extends Roo.bootstrap.Component
35058  * Bootstrap DocumentManager class
35059  * @cfg {String} paramName default 'imageUpload'
35060  * @cfg {String} toolTipName default 'filename'
35061  * @cfg {String} method default POST
35062  * @cfg {String} url action url
35063  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
35064  * @cfg {Boolean} multiple multiple upload default true
35065  * @cfg {Number} thumbSize default 300
35066  * @cfg {String} fieldLabel
35067  * @cfg {Number} labelWidth default 4
35068  * @cfg {String} labelAlign (left|top) default left
35069  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
35070 * @cfg {Number} labellg set the width of label (1-12)
35071  * @cfg {Number} labelmd set the width of label (1-12)
35072  * @cfg {Number} labelsm set the width of label (1-12)
35073  * @cfg {Number} labelxs set the width of label (1-12)
35074  * 
35075  * @constructor
35076  * Create a new DocumentManager
35077  * @param {Object} config The config object
35078  */
35079
35080 Roo.bootstrap.DocumentManager = function(config){
35081     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
35082     
35083     this.files = [];
35084     this.delegates = [];
35085     
35086     this.addEvents({
35087         /**
35088          * @event initial
35089          * Fire when initial the DocumentManager
35090          * @param {Roo.bootstrap.DocumentManager} this
35091          */
35092         "initial" : true,
35093         /**
35094          * @event inspect
35095          * inspect selected file
35096          * @param {Roo.bootstrap.DocumentManager} this
35097          * @param {File} file
35098          */
35099         "inspect" : true,
35100         /**
35101          * @event exception
35102          * Fire when xhr load exception
35103          * @param {Roo.bootstrap.DocumentManager} this
35104          * @param {XMLHttpRequest} xhr
35105          */
35106         "exception" : true,
35107         /**
35108          * @event afterupload
35109          * Fire when xhr load exception
35110          * @param {Roo.bootstrap.DocumentManager} this
35111          * @param {XMLHttpRequest} xhr
35112          */
35113         "afterupload" : true,
35114         /**
35115          * @event prepare
35116          * prepare the form data
35117          * @param {Roo.bootstrap.DocumentManager} this
35118          * @param {Object} formData
35119          */
35120         "prepare" : true,
35121         /**
35122          * @event remove
35123          * Fire when remove the file
35124          * @param {Roo.bootstrap.DocumentManager} this
35125          * @param {Object} file
35126          */
35127         "remove" : true,
35128         /**
35129          * @event refresh
35130          * Fire after refresh the file
35131          * @param {Roo.bootstrap.DocumentManager} this
35132          */
35133         "refresh" : true,
35134         /**
35135          * @event click
35136          * Fire after click the image
35137          * @param {Roo.bootstrap.DocumentManager} this
35138          * @param {Object} file
35139          */
35140         "click" : true,
35141         /**
35142          * @event edit
35143          * Fire when upload a image and editable set to true
35144          * @param {Roo.bootstrap.DocumentManager} this
35145          * @param {Object} file
35146          */
35147         "edit" : true,
35148         /**
35149          * @event beforeselectfile
35150          * Fire before select file
35151          * @param {Roo.bootstrap.DocumentManager} this
35152          */
35153         "beforeselectfile" : true,
35154         /**
35155          * @event process
35156          * Fire before process file
35157          * @param {Roo.bootstrap.DocumentManager} this
35158          * @param {Object} file
35159          */
35160         "process" : true,
35161         /**
35162          * @event previewrendered
35163          * Fire when preview rendered
35164          * @param {Roo.bootstrap.DocumentManager} this
35165          * @param {Object} file
35166          */
35167         "previewrendered" : true,
35168         /**
35169          */
35170         "previewResize" : true
35171         
35172     });
35173 };
35174
35175 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
35176     
35177     boxes : 0,
35178     inputName : '',
35179     thumbSize : 300,
35180     multiple : true,
35181     files : false,
35182     method : 'POST',
35183     url : '',
35184     paramName : 'imageUpload',
35185     toolTipName : 'filename',
35186     fieldLabel : '',
35187     labelWidth : 4,
35188     labelAlign : 'left',
35189     editable : true,
35190     delegates : false,
35191     xhr : false, 
35192     
35193     labellg : 0,
35194     labelmd : 0,
35195     labelsm : 0,
35196     labelxs : 0,
35197     
35198     getAutoCreate : function()
35199     {   
35200         var managerWidget = {
35201             tag : 'div',
35202             cls : 'roo-document-manager',
35203             cn : [
35204                 {
35205                     tag : 'input',
35206                     cls : 'roo-document-manager-selector',
35207                     type : 'file'
35208                 },
35209                 {
35210                     tag : 'div',
35211                     cls : 'roo-document-manager-uploader',
35212                     cn : [
35213                         {
35214                             tag : 'div',
35215                             cls : 'roo-document-manager-upload-btn',
35216                             html : '<i class="fa fa-plus"></i>'
35217                         }
35218                     ]
35219                     
35220                 }
35221             ]
35222         };
35223         
35224         var content = [
35225             {
35226                 tag : 'div',
35227                 cls : 'column col-md-12',
35228                 cn : managerWidget
35229             }
35230         ];
35231         
35232         if(this.fieldLabel.length){
35233             
35234             content = [
35235                 {
35236                     tag : 'div',
35237                     cls : 'column col-md-12',
35238                     html : this.fieldLabel
35239                 },
35240                 {
35241                     tag : 'div',
35242                     cls : 'column col-md-12',
35243                     cn : managerWidget
35244                 }
35245             ];
35246
35247             if(this.labelAlign == 'left'){
35248                 content = [
35249                     {
35250                         tag : 'div',
35251                         cls : 'column',
35252                         html : this.fieldLabel
35253                     },
35254                     {
35255                         tag : 'div',
35256                         cls : 'column',
35257                         cn : managerWidget
35258                     }
35259                 ];
35260                 
35261                 if(this.labelWidth > 12){
35262                     content[0].style = "width: " + this.labelWidth + 'px';
35263                 }
35264
35265                 if(this.labelWidth < 13 && this.labelmd == 0){
35266                     this.labelmd = this.labelWidth;
35267                 }
35268
35269                 if(this.labellg > 0){
35270                     content[0].cls += ' col-lg-' + this.labellg;
35271                     content[1].cls += ' col-lg-' + (12 - this.labellg);
35272                 }
35273
35274                 if(this.labelmd > 0){
35275                     content[0].cls += ' col-md-' + this.labelmd;
35276                     content[1].cls += ' col-md-' + (12 - this.labelmd);
35277                 }
35278
35279                 if(this.labelsm > 0){
35280                     content[0].cls += ' col-sm-' + this.labelsm;
35281                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
35282                 }
35283
35284                 if(this.labelxs > 0){
35285                     content[0].cls += ' col-xs-' + this.labelxs;
35286                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
35287                 }
35288                 
35289             }
35290         }
35291         
35292         var cfg = {
35293             tag : 'div',
35294             cls : 'row clearfix',
35295             cn : content
35296         };
35297         
35298         return cfg;
35299         
35300     },
35301     
35302     initEvents : function()
35303     {
35304         this.managerEl = this.el.select('.roo-document-manager', true).first();
35305         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35306         
35307         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
35308         this.selectorEl.hide();
35309         
35310         if(this.multiple){
35311             this.selectorEl.attr('multiple', 'multiple');
35312         }
35313         
35314         this.selectorEl.on('change', this.onFileSelected, this);
35315         
35316         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
35317         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35318         
35319         this.uploader.on('click', this.onUploaderClick, this);
35320         
35321         this.renderProgressDialog();
35322         
35323         var _this = this;
35324         
35325         window.addEventListener("resize", function() { _this.refresh(); } );
35326         
35327         this.fireEvent('initial', this);
35328     },
35329     
35330     renderProgressDialog : function()
35331     {
35332         var _this = this;
35333         
35334         this.progressDialog = new Roo.bootstrap.Modal({
35335             cls : 'roo-document-manager-progress-dialog',
35336             allow_close : false,
35337             animate : false,
35338             title : '',
35339             buttons : [
35340                 {
35341                     name  :'cancel',
35342                     weight : 'danger',
35343                     html : 'Cancel'
35344                 }
35345             ], 
35346             listeners : { 
35347                 btnclick : function() {
35348                     _this.uploadCancel();
35349                     this.hide();
35350                 }
35351             }
35352         });
35353          
35354         this.progressDialog.render(Roo.get(document.body));
35355          
35356         this.progress = new Roo.bootstrap.Progress({
35357             cls : 'roo-document-manager-progress',
35358             active : true,
35359             striped : true
35360         });
35361         
35362         this.progress.render(this.progressDialog.getChildContainer());
35363         
35364         this.progressBar = new Roo.bootstrap.ProgressBar({
35365             cls : 'roo-document-manager-progress-bar',
35366             aria_valuenow : 0,
35367             aria_valuemin : 0,
35368             aria_valuemax : 12,
35369             panel : 'success'
35370         });
35371         
35372         this.progressBar.render(this.progress.getChildContainer());
35373     },
35374     
35375     onUploaderClick : function(e)
35376     {
35377         e.preventDefault();
35378      
35379         if(this.fireEvent('beforeselectfile', this) != false){
35380             this.selectorEl.dom.click();
35381         }
35382         
35383     },
35384     
35385     onFileSelected : function(e)
35386     {
35387         e.preventDefault();
35388         
35389         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35390             return;
35391         }
35392         
35393         Roo.each(this.selectorEl.dom.files, function(file){
35394             if(this.fireEvent('inspect', this, file) != false){
35395                 this.files.push(file);
35396             }
35397         }, this);
35398         
35399         this.queue();
35400         
35401     },
35402     
35403     queue : function()
35404     {
35405         this.selectorEl.dom.value = '';
35406         
35407         if(!this.files || !this.files.length){
35408             return;
35409         }
35410         
35411         if(this.boxes > 0 && this.files.length > this.boxes){
35412             this.files = this.files.slice(0, this.boxes);
35413         }
35414         
35415         this.uploader.show();
35416         
35417         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35418             this.uploader.hide();
35419         }
35420         
35421         var _this = this;
35422         
35423         var files = [];
35424         
35425         var docs = [];
35426         
35427         Roo.each(this.files, function(file){
35428             
35429             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35430                 var f = this.renderPreview(file);
35431                 files.push(f);
35432                 return;
35433             }
35434             
35435             if(file.type.indexOf('image') != -1){
35436                 this.delegates.push(
35437                     (function(){
35438                         _this.process(file);
35439                     }).createDelegate(this)
35440                 );
35441         
35442                 return;
35443             }
35444             
35445             docs.push(
35446                 (function(){
35447                     _this.process(file);
35448                 }).createDelegate(this)
35449             );
35450             
35451         }, this);
35452         
35453         this.files = files;
35454         
35455         this.delegates = this.delegates.concat(docs);
35456         
35457         if(!this.delegates.length){
35458             this.refresh();
35459             return;
35460         }
35461         
35462         this.progressBar.aria_valuemax = this.delegates.length;
35463         
35464         this.arrange();
35465         
35466         return;
35467     },
35468     
35469     arrange : function()
35470     {
35471         if(!this.delegates.length){
35472             this.progressDialog.hide();
35473             this.refresh();
35474             return;
35475         }
35476         
35477         var delegate = this.delegates.shift();
35478         
35479         this.progressDialog.show();
35480         
35481         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
35482         
35483         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
35484         
35485         delegate();
35486     },
35487     
35488     refresh : function()
35489     {
35490         this.uploader.show();
35491         
35492         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35493             this.uploader.hide();
35494         }
35495         
35496         Roo.isTouch ? this.closable(false) : this.closable(true);
35497         
35498         this.fireEvent('refresh', this);
35499     },
35500     
35501     onRemove : function(e, el, o)
35502     {
35503         e.preventDefault();
35504         
35505         this.fireEvent('remove', this, o);
35506         
35507     },
35508     
35509     remove : function(o)
35510     {
35511         var files = [];
35512         
35513         Roo.each(this.files, function(file){
35514             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
35515                 files.push(file);
35516                 return;
35517             }
35518
35519             o.target.remove();
35520
35521         }, this);
35522         
35523         this.files = files;
35524         
35525         this.refresh();
35526     },
35527     
35528     clear : function()
35529     {
35530         Roo.each(this.files, function(file){
35531             if(!file.target){
35532                 return;
35533             }
35534             
35535             file.target.remove();
35536
35537         }, this);
35538         
35539         this.files = [];
35540         
35541         this.refresh();
35542     },
35543     
35544     onClick : function(e, el, o)
35545     {
35546         e.preventDefault();
35547         
35548         this.fireEvent('click', this, o);
35549         
35550     },
35551     
35552     closable : function(closable)
35553     {
35554         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
35555             
35556             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35557             
35558             if(closable){
35559                 el.show();
35560                 return;
35561             }
35562             
35563             el.hide();
35564             
35565         }, this);
35566     },
35567     
35568     xhrOnLoad : function(xhr)
35569     {
35570         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35571             el.remove();
35572         }, this);
35573         
35574         if (xhr.readyState !== 4) {
35575             this.arrange();
35576             this.fireEvent('exception', this, xhr);
35577             return;
35578         }
35579
35580         var response = Roo.decode(xhr.responseText);
35581         
35582         if(!response.success){
35583             this.arrange();
35584             this.fireEvent('exception', this, xhr);
35585             return;
35586         }
35587         
35588         var file = this.renderPreview(response.data);
35589         
35590         this.files.push(file);
35591         
35592         this.arrange();
35593         
35594         this.fireEvent('afterupload', this, xhr);
35595         
35596     },
35597     
35598     xhrOnError : function(xhr)
35599     {
35600         Roo.log('xhr on error');
35601         
35602         var response = Roo.decode(xhr.responseText);
35603           
35604         Roo.log(response);
35605         
35606         this.arrange();
35607     },
35608     
35609     process : function(file)
35610     {
35611         if(this.fireEvent('process', this, file) !== false){
35612             if(this.editable && file.type.indexOf('image') != -1){
35613                 this.fireEvent('edit', this, file);
35614                 return;
35615             }
35616
35617             this.uploadStart(file, false);
35618
35619             return;
35620         }
35621         
35622     },
35623     
35624     uploadStart : function(file, crop)
35625     {
35626         this.xhr = new XMLHttpRequest();
35627         
35628         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35629             this.arrange();
35630             return;
35631         }
35632         
35633         file.xhr = this.xhr;
35634             
35635         this.managerEl.createChild({
35636             tag : 'div',
35637             cls : 'roo-document-manager-loading',
35638             cn : [
35639                 {
35640                     tag : 'div',
35641                     tooltip : file.name,
35642                     cls : 'roo-document-manager-thumb',
35643                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35644                 }
35645             ]
35646
35647         });
35648
35649         this.xhr.open(this.method, this.url, true);
35650         
35651         var headers = {
35652             "Accept": "application/json",
35653             "Cache-Control": "no-cache",
35654             "X-Requested-With": "XMLHttpRequest"
35655         };
35656         
35657         for (var headerName in headers) {
35658             var headerValue = headers[headerName];
35659             if (headerValue) {
35660                 this.xhr.setRequestHeader(headerName, headerValue);
35661             }
35662         }
35663         
35664         var _this = this;
35665         
35666         this.xhr.onload = function()
35667         {
35668             _this.xhrOnLoad(_this.xhr);
35669         }
35670         
35671         this.xhr.onerror = function()
35672         {
35673             _this.xhrOnError(_this.xhr);
35674         }
35675         
35676         var formData = new FormData();
35677
35678         formData.append('returnHTML', 'NO');
35679         
35680         if(crop){
35681             formData.append('crop', crop);
35682         }
35683         
35684         formData.append(this.paramName, file, file.name);
35685         
35686         var options = {
35687             file : file, 
35688             manually : false
35689         };
35690         
35691         if(this.fireEvent('prepare', this, formData, options) != false){
35692             
35693             if(options.manually){
35694                 return;
35695             }
35696             
35697             this.xhr.send(formData);
35698             return;
35699         };
35700         
35701         this.uploadCancel();
35702     },
35703     
35704     uploadCancel : function()
35705     {
35706         if (this.xhr) {
35707             this.xhr.abort();
35708         }
35709         
35710         this.delegates = [];
35711         
35712         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35713             el.remove();
35714         }, this);
35715         
35716         this.arrange();
35717     },
35718     
35719     renderPreview : function(file)
35720     {
35721         if(typeof(file.target) != 'undefined' && file.target){
35722             return file;
35723         }
35724         
35725         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
35726         
35727         var previewEl = this.managerEl.createChild({
35728             tag : 'div',
35729             cls : 'roo-document-manager-preview',
35730             cn : [
35731                 {
35732                     tag : 'div',
35733                     tooltip : file[this.toolTipName],
35734                     cls : 'roo-document-manager-thumb',
35735                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
35736                 },
35737                 {
35738                     tag : 'button',
35739                     cls : 'close',
35740                     html : '<i class="fa fa-times-circle"></i>'
35741                 }
35742             ]
35743         });
35744
35745         var close = previewEl.select('button.close', true).first();
35746
35747         close.on('click', this.onRemove, this, file);
35748
35749         file.target = previewEl;
35750
35751         var image = previewEl.select('img', true).first();
35752         
35753         var _this = this;
35754         
35755         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
35756         
35757         image.on('click', this.onClick, this, file);
35758         
35759         this.fireEvent('previewrendered', this, file);
35760         
35761         return file;
35762         
35763     },
35764     
35765     onPreviewLoad : function(file, image)
35766     {
35767         if(typeof(file.target) == 'undefined' || !file.target){
35768             return;
35769         }
35770         
35771         var width = image.dom.naturalWidth || image.dom.width;
35772         var height = image.dom.naturalHeight || image.dom.height;
35773         
35774         if(!this.previewResize) {
35775             return;
35776         }
35777         
35778         if(width > height){
35779             file.target.addClass('wide');
35780             return;
35781         }
35782         
35783         file.target.addClass('tall');
35784         return;
35785         
35786     },
35787     
35788     uploadFromSource : function(file, crop)
35789     {
35790         this.xhr = new XMLHttpRequest();
35791         
35792         this.managerEl.createChild({
35793             tag : 'div',
35794             cls : 'roo-document-manager-loading',
35795             cn : [
35796                 {
35797                     tag : 'div',
35798                     tooltip : file.name,
35799                     cls : 'roo-document-manager-thumb',
35800                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35801                 }
35802             ]
35803
35804         });
35805
35806         this.xhr.open(this.method, this.url, true);
35807         
35808         var headers = {
35809             "Accept": "application/json",
35810             "Cache-Control": "no-cache",
35811             "X-Requested-With": "XMLHttpRequest"
35812         };
35813         
35814         for (var headerName in headers) {
35815             var headerValue = headers[headerName];
35816             if (headerValue) {
35817                 this.xhr.setRequestHeader(headerName, headerValue);
35818             }
35819         }
35820         
35821         var _this = this;
35822         
35823         this.xhr.onload = function()
35824         {
35825             _this.xhrOnLoad(_this.xhr);
35826         }
35827         
35828         this.xhr.onerror = function()
35829         {
35830             _this.xhrOnError(_this.xhr);
35831         }
35832         
35833         var formData = new FormData();
35834
35835         formData.append('returnHTML', 'NO');
35836         
35837         formData.append('crop', crop);
35838         
35839         if(typeof(file.filename) != 'undefined'){
35840             formData.append('filename', file.filename);
35841         }
35842         
35843         if(typeof(file.mimetype) != 'undefined'){
35844             formData.append('mimetype', file.mimetype);
35845         }
35846         
35847         Roo.log(formData);
35848         
35849         if(this.fireEvent('prepare', this, formData) != false){
35850             this.xhr.send(formData);
35851         };
35852     }
35853 });
35854
35855 /*
35856 * Licence: LGPL
35857 */
35858
35859 /**
35860  * @class Roo.bootstrap.DocumentViewer
35861  * @extends Roo.bootstrap.Component
35862  * Bootstrap DocumentViewer class
35863  * @cfg {Boolean} showDownload (true|false) show download button (default true)
35864  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
35865  * 
35866  * @constructor
35867  * Create a new DocumentViewer
35868  * @param {Object} config The config object
35869  */
35870
35871 Roo.bootstrap.DocumentViewer = function(config){
35872     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
35873     
35874     this.addEvents({
35875         /**
35876          * @event initial
35877          * Fire after initEvent
35878          * @param {Roo.bootstrap.DocumentViewer} this
35879          */
35880         "initial" : true,
35881         /**
35882          * @event click
35883          * Fire after click
35884          * @param {Roo.bootstrap.DocumentViewer} this
35885          */
35886         "click" : true,
35887         /**
35888          * @event download
35889          * Fire after download button
35890          * @param {Roo.bootstrap.DocumentViewer} this
35891          */
35892         "download" : true,
35893         /**
35894          * @event trash
35895          * Fire after trash button
35896          * @param {Roo.bootstrap.DocumentViewer} this
35897          */
35898         "trash" : true
35899         
35900     });
35901 };
35902
35903 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
35904     
35905     showDownload : true,
35906     
35907     showTrash : true,
35908     
35909     getAutoCreate : function()
35910     {
35911         var cfg = {
35912             tag : 'div',
35913             cls : 'roo-document-viewer',
35914             cn : [
35915                 {
35916                     tag : 'div',
35917                     cls : 'roo-document-viewer-body',
35918                     cn : [
35919                         {
35920                             tag : 'div',
35921                             cls : 'roo-document-viewer-thumb',
35922                             cn : [
35923                                 {
35924                                     tag : 'img',
35925                                     cls : 'roo-document-viewer-image'
35926                                 }
35927                             ]
35928                         }
35929                     ]
35930                 },
35931                 {
35932                     tag : 'div',
35933                     cls : 'roo-document-viewer-footer',
35934                     cn : {
35935                         tag : 'div',
35936                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
35937                         cn : [
35938                             {
35939                                 tag : 'div',
35940                                 cls : 'btn-group roo-document-viewer-download',
35941                                 cn : [
35942                                     {
35943                                         tag : 'button',
35944                                         cls : 'btn btn-default',
35945                                         html : '<i class="fa fa-download"></i>'
35946                                     }
35947                                 ]
35948                             },
35949                             {
35950                                 tag : 'div',
35951                                 cls : 'btn-group roo-document-viewer-trash',
35952                                 cn : [
35953                                     {
35954                                         tag : 'button',
35955                                         cls : 'btn btn-default',
35956                                         html : '<i class="fa fa-trash"></i>'
35957                                     }
35958                                 ]
35959                             }
35960                         ]
35961                     }
35962                 }
35963             ]
35964         };
35965         
35966         return cfg;
35967     },
35968     
35969     initEvents : function()
35970     {
35971         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
35972         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35973         
35974         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
35975         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35976         
35977         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
35978         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35979         
35980         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
35981         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
35982         
35983         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
35984         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
35985         
35986         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
35987         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
35988         
35989         this.bodyEl.on('click', this.onClick, this);
35990         this.downloadBtn.on('click', this.onDownload, this);
35991         this.trashBtn.on('click', this.onTrash, this);
35992         
35993         this.downloadBtn.hide();
35994         this.trashBtn.hide();
35995         
35996         if(this.showDownload){
35997             this.downloadBtn.show();
35998         }
35999         
36000         if(this.showTrash){
36001             this.trashBtn.show();
36002         }
36003         
36004         if(!this.showDownload && !this.showTrash) {
36005             this.footerEl.hide();
36006         }
36007         
36008     },
36009     
36010     initial : function()
36011     {
36012         this.fireEvent('initial', this);
36013         
36014     },
36015     
36016     onClick : function(e)
36017     {
36018         e.preventDefault();
36019         
36020         this.fireEvent('click', this);
36021     },
36022     
36023     onDownload : function(e)
36024     {
36025         e.preventDefault();
36026         
36027         this.fireEvent('download', this);
36028     },
36029     
36030     onTrash : function(e)
36031     {
36032         e.preventDefault();
36033         
36034         this.fireEvent('trash', this);
36035     }
36036     
36037 });
36038 /*
36039  * - LGPL
36040  *
36041  * FieldLabel
36042  * 
36043  */
36044
36045 /**
36046  * @class Roo.bootstrap.form.FieldLabel
36047  * @extends Roo.bootstrap.Component
36048  * Bootstrap FieldLabel class
36049  * @cfg {String} html contents of the element
36050  * @cfg {String} tag tag of the element default label
36051  * @cfg {String} cls class of the element
36052  * @cfg {String} target label target 
36053  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
36054  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
36055  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
36056  * @cfg {String} iconTooltip default "This field is required"
36057  * @cfg {String} indicatorpos (left|right) default left
36058  * 
36059  * @constructor
36060  * Create a new FieldLabel
36061  * @param {Object} config The config object
36062  */
36063
36064 Roo.bootstrap.form.FieldLabel = function(config){
36065     Roo.bootstrap.Element.superclass.constructor.call(this, config);
36066     
36067     this.addEvents({
36068             /**
36069              * @event invalid
36070              * Fires after the field has been marked as invalid.
36071              * @param {Roo.form.FieldLabel} this
36072              * @param {String} msg The validation message
36073              */
36074             invalid : true,
36075             /**
36076              * @event valid
36077              * Fires after the field has been validated with no errors.
36078              * @param {Roo.form.FieldLabel} this
36079              */
36080             valid : true
36081         });
36082 };
36083
36084 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
36085     
36086     tag: 'label',
36087     cls: '',
36088     html: '',
36089     target: '',
36090     allowBlank : true,
36091     invalidClass : 'has-warning',
36092     validClass : 'has-success',
36093     iconTooltip : 'This field is required',
36094     indicatorpos : 'left',
36095     
36096     getAutoCreate : function(){
36097         
36098         var cls = "";
36099         if (!this.allowBlank) {
36100             cls  = "visible";
36101         }
36102         
36103         var cfg = {
36104             tag : this.tag,
36105             cls : 'roo-bootstrap-field-label ' + this.cls,
36106             for : this.target,
36107             cn : [
36108                 {
36109                     tag : 'i',
36110                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
36111                     tooltip : this.iconTooltip
36112                 },
36113                 {
36114                     tag : 'span',
36115                     html : this.html
36116                 }
36117             ] 
36118         };
36119         
36120         if(this.indicatorpos == 'right'){
36121             var cfg = {
36122                 tag : this.tag,
36123                 cls : 'roo-bootstrap-field-label ' + this.cls,
36124                 for : this.target,
36125                 cn : [
36126                     {
36127                         tag : 'span',
36128                         html : this.html
36129                     },
36130                     {
36131                         tag : 'i',
36132                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
36133                         tooltip : this.iconTooltip
36134                     }
36135                 ] 
36136             };
36137         }
36138         
36139         return cfg;
36140     },
36141     
36142     initEvents: function() 
36143     {
36144         Roo.bootstrap.Element.superclass.initEvents.call(this);
36145         
36146         this.indicator = this.indicatorEl();
36147         
36148         if(this.indicator){
36149             this.indicator.removeClass('visible');
36150             this.indicator.addClass('invisible');
36151         }
36152         
36153         Roo.bootstrap.form.FieldLabel.register(this);
36154     },
36155     
36156     indicatorEl : function()
36157     {
36158         var indicator = this.el.select('i.roo-required-indicator',true).first();
36159         
36160         if(!indicator){
36161             return false;
36162         }
36163         
36164         return indicator;
36165         
36166     },
36167     
36168     /**
36169      * Mark this field as valid
36170      */
36171     markValid : function()
36172     {
36173         if(this.indicator){
36174             this.indicator.removeClass('visible');
36175             this.indicator.addClass('invisible');
36176         }
36177         if (Roo.bootstrap.version == 3) {
36178             this.el.removeClass(this.invalidClass);
36179             this.el.addClass(this.validClass);
36180         } else {
36181             this.el.removeClass('is-invalid');
36182             this.el.addClass('is-valid');
36183         }
36184         
36185         
36186         this.fireEvent('valid', this);
36187     },
36188     
36189     /**
36190      * Mark this field as invalid
36191      * @param {String} msg The validation message
36192      */
36193     markInvalid : function(msg)
36194     {
36195         if(this.indicator){
36196             this.indicator.removeClass('invisible');
36197             this.indicator.addClass('visible');
36198         }
36199           if (Roo.bootstrap.version == 3) {
36200             this.el.removeClass(this.validClass);
36201             this.el.addClass(this.invalidClass);
36202         } else {
36203             this.el.removeClass('is-valid');
36204             this.el.addClass('is-invalid');
36205         }
36206         
36207         
36208         this.fireEvent('invalid', this, msg);
36209     }
36210     
36211    
36212 });
36213
36214 Roo.apply(Roo.bootstrap.form.FieldLabel, {
36215     
36216     groups: {},
36217     
36218      /**
36219     * register a FieldLabel Group
36220     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
36221     */
36222     register : function(label)
36223     {
36224         if(this.groups.hasOwnProperty(label.target)){
36225             return;
36226         }
36227      
36228         this.groups[label.target] = label;
36229         
36230     },
36231     /**
36232     * fetch a FieldLabel Group based on the target
36233     * @param {string} target
36234     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
36235     */
36236     get: function(target) {
36237         if (typeof(this.groups[target]) == 'undefined') {
36238             return false;
36239         }
36240         
36241         return this.groups[target] ;
36242     }
36243 });
36244
36245  
36246
36247  /*
36248  * - LGPL
36249  *
36250  * page DateSplitField.
36251  * 
36252  */
36253
36254
36255 /**
36256  * @class Roo.bootstrap.form.DateSplitField
36257  * @extends Roo.bootstrap.Component
36258  * Bootstrap DateSplitField class
36259  * @cfg {string} fieldLabel - the label associated
36260  * @cfg {Number} labelWidth set the width of label (0-12)
36261  * @cfg {String} labelAlign (top|left)
36262  * @cfg {Boolean} dayAllowBlank (true|false) default false
36263  * @cfg {Boolean} monthAllowBlank (true|false) default false
36264  * @cfg {Boolean} yearAllowBlank (true|false) default false
36265  * @cfg {string} dayPlaceholder 
36266  * @cfg {string} monthPlaceholder
36267  * @cfg {string} yearPlaceholder
36268  * @cfg {string} dayFormat default 'd'
36269  * @cfg {string} monthFormat default 'm'
36270  * @cfg {string} yearFormat default 'Y'
36271  * @cfg {Number} labellg set the width of label (1-12)
36272  * @cfg {Number} labelmd set the width of label (1-12)
36273  * @cfg {Number} labelsm set the width of label (1-12)
36274  * @cfg {Number} labelxs set the width of label (1-12)
36275
36276  *     
36277  * @constructor
36278  * Create a new DateSplitField
36279  * @param {Object} config The config object
36280  */
36281
36282 Roo.bootstrap.form.DateSplitField = function(config){
36283     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
36284     
36285     this.addEvents({
36286         // raw events
36287          /**
36288          * @event years
36289          * getting the data of years
36290          * @param {Roo.bootstrap.form.DateSplitField} this
36291          * @param {Object} years
36292          */
36293         "years" : true,
36294         /**
36295          * @event days
36296          * getting the data of days
36297          * @param {Roo.bootstrap.form.DateSplitField} this
36298          * @param {Object} days
36299          */
36300         "days" : true,
36301         /**
36302          * @event invalid
36303          * Fires after the field has been marked as invalid.
36304          * @param {Roo.form.Field} this
36305          * @param {String} msg The validation message
36306          */
36307         invalid : true,
36308        /**
36309          * @event valid
36310          * Fires after the field has been validated with no errors.
36311          * @param {Roo.form.Field} this
36312          */
36313         valid : true
36314     });
36315 };
36316
36317 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
36318     
36319     fieldLabel : '',
36320     labelAlign : 'top',
36321     labelWidth : 3,
36322     dayAllowBlank : false,
36323     monthAllowBlank : false,
36324     yearAllowBlank : false,
36325     dayPlaceholder : '',
36326     monthPlaceholder : '',
36327     yearPlaceholder : '',
36328     dayFormat : 'd',
36329     monthFormat : 'm',
36330     yearFormat : 'Y',
36331     isFormField : true,
36332     labellg : 0,
36333     labelmd : 0,
36334     labelsm : 0,
36335     labelxs : 0,
36336     
36337     getAutoCreate : function()
36338     {
36339         var cfg = {
36340             tag : 'div',
36341             cls : 'row roo-date-split-field-group',
36342             cn : [
36343                 {
36344                     tag : 'input',
36345                     type : 'hidden',
36346                     cls : 'form-hidden-field roo-date-split-field-group-value',
36347                     name : this.name
36348                 }
36349             ]
36350         };
36351         
36352         var labelCls = 'col-md-12';
36353         var contentCls = 'col-md-4';
36354         
36355         if(this.fieldLabel){
36356             
36357             var label = {
36358                 tag : 'div',
36359                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
36360                 cn : [
36361                     {
36362                         tag : 'label',
36363                         html : this.fieldLabel
36364                     }
36365                 ]
36366             };
36367             
36368             if(this.labelAlign == 'left'){
36369             
36370                 if(this.labelWidth > 12){
36371                     label.style = "width: " + this.labelWidth + 'px';
36372                 }
36373
36374                 if(this.labelWidth < 13 && this.labelmd == 0){
36375                     this.labelmd = this.labelWidth;
36376                 }
36377
36378                 if(this.labellg > 0){
36379                     labelCls = ' col-lg-' + this.labellg;
36380                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
36381                 }
36382
36383                 if(this.labelmd > 0){
36384                     labelCls = ' col-md-' + this.labelmd;
36385                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
36386                 }
36387
36388                 if(this.labelsm > 0){
36389                     labelCls = ' col-sm-' + this.labelsm;
36390                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
36391                 }
36392
36393                 if(this.labelxs > 0){
36394                     labelCls = ' col-xs-' + this.labelxs;
36395                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
36396                 }
36397             }
36398             
36399             label.cls += ' ' + labelCls;
36400             
36401             cfg.cn.push(label);
36402         }
36403         
36404         Roo.each(['day', 'month', 'year'], function(t){
36405             cfg.cn.push({
36406                 tag : 'div',
36407                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
36408             });
36409         }, this);
36410         
36411         return cfg;
36412     },
36413     
36414     inputEl: function ()
36415     {
36416         return this.el.select('.roo-date-split-field-group-value', true).first();
36417     },
36418     
36419     onRender : function(ct, position) 
36420     {
36421         var _this = this;
36422         
36423         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
36424         
36425         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
36426         
36427         this.dayField = new Roo.bootstrap.form.ComboBox({
36428             allowBlank : this.dayAllowBlank,
36429             alwaysQuery : true,
36430             displayField : 'value',
36431             editable : false,
36432             fieldLabel : '',
36433             forceSelection : true,
36434             mode : 'local',
36435             placeholder : this.dayPlaceholder,
36436             selectOnFocus : true,
36437             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36438             triggerAction : 'all',
36439             typeAhead : true,
36440             valueField : 'value',
36441             store : new Roo.data.SimpleStore({
36442                 data : (function() {    
36443                     var days = [];
36444                     _this.fireEvent('days', _this, days);
36445                     return days;
36446                 })(),
36447                 fields : [ 'value' ]
36448             }),
36449             listeners : {
36450                 select : function (_self, record, index)
36451                 {
36452                     _this.setValue(_this.getValue());
36453                 }
36454             }
36455         });
36456
36457         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
36458         
36459         this.monthField = new Roo.bootstrap.form.MonthField({
36460             after : '<i class=\"fa fa-calendar\"></i>',
36461             allowBlank : this.monthAllowBlank,
36462             placeholder : this.monthPlaceholder,
36463             readOnly : true,
36464             listeners : {
36465                 render : function (_self)
36466                 {
36467                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
36468                         e.preventDefault();
36469                         _self.focus();
36470                     });
36471                 },
36472                 select : function (_self, oldvalue, newvalue)
36473                 {
36474                     _this.setValue(_this.getValue());
36475                 }
36476             }
36477         });
36478         
36479         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
36480         
36481         this.yearField = new Roo.bootstrap.form.ComboBox({
36482             allowBlank : this.yearAllowBlank,
36483             alwaysQuery : true,
36484             displayField : 'value',
36485             editable : false,
36486             fieldLabel : '',
36487             forceSelection : true,
36488             mode : 'local',
36489             placeholder : this.yearPlaceholder,
36490             selectOnFocus : true,
36491             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36492             triggerAction : 'all',
36493             typeAhead : true,
36494             valueField : 'value',
36495             store : new Roo.data.SimpleStore({
36496                 data : (function() {
36497                     var years = [];
36498                     _this.fireEvent('years', _this, years);
36499                     return years;
36500                 })(),
36501                 fields : [ 'value' ]
36502             }),
36503             listeners : {
36504                 select : function (_self, record, index)
36505                 {
36506                     _this.setValue(_this.getValue());
36507                 }
36508             }
36509         });
36510
36511         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
36512     },
36513     
36514     setValue : function(v, format)
36515     {
36516         this.inputEl.dom.value = v;
36517         
36518         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
36519         
36520         var d = Date.parseDate(v, f);
36521         
36522         if(!d){
36523             this.validate();
36524             return;
36525         }
36526         
36527         this.setDay(d.format(this.dayFormat));
36528         this.setMonth(d.format(this.monthFormat));
36529         this.setYear(d.format(this.yearFormat));
36530         
36531         this.validate();
36532         
36533         return;
36534     },
36535     
36536     setDay : function(v)
36537     {
36538         this.dayField.setValue(v);
36539         this.inputEl.dom.value = this.getValue();
36540         this.validate();
36541         return;
36542     },
36543     
36544     setMonth : function(v)
36545     {
36546         this.monthField.setValue(v, true);
36547         this.inputEl.dom.value = this.getValue();
36548         this.validate();
36549         return;
36550     },
36551     
36552     setYear : function(v)
36553     {
36554         this.yearField.setValue(v);
36555         this.inputEl.dom.value = this.getValue();
36556         this.validate();
36557         return;
36558     },
36559     
36560     getDay : function()
36561     {
36562         return this.dayField.getValue();
36563     },
36564     
36565     getMonth : function()
36566     {
36567         return this.monthField.getValue();
36568     },
36569     
36570     getYear : function()
36571     {
36572         return this.yearField.getValue();
36573     },
36574     
36575     getValue : function()
36576     {
36577         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
36578         
36579         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
36580         
36581         return date;
36582     },
36583     
36584     reset : function()
36585     {
36586         this.setDay('');
36587         this.setMonth('');
36588         this.setYear('');
36589         this.inputEl.dom.value = '';
36590         this.validate();
36591         return;
36592     },
36593     
36594     validate : function()
36595     {
36596         var d = this.dayField.validate();
36597         var m = this.monthField.validate();
36598         var y = this.yearField.validate();
36599         
36600         var valid = true;
36601         
36602         if(
36603                 (!this.dayAllowBlank && !d) ||
36604                 (!this.monthAllowBlank && !m) ||
36605                 (!this.yearAllowBlank && !y)
36606         ){
36607             valid = false;
36608         }
36609         
36610         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
36611             return valid;
36612         }
36613         
36614         if(valid){
36615             this.markValid();
36616             return valid;
36617         }
36618         
36619         this.markInvalid();
36620         
36621         return valid;
36622     },
36623     
36624     markValid : function()
36625     {
36626         
36627         var label = this.el.select('label', true).first();
36628         var icon = this.el.select('i.fa-star', true).first();
36629
36630         if(label && icon){
36631             icon.remove();
36632         }
36633         
36634         this.fireEvent('valid', this);
36635     },
36636     
36637      /**
36638      * Mark this field as invalid
36639      * @param {String} msg The validation message
36640      */
36641     markInvalid : function(msg)
36642     {
36643         
36644         var label = this.el.select('label', true).first();
36645         var icon = this.el.select('i.fa-star', true).first();
36646
36647         if(label && !icon){
36648             this.el.select('.roo-date-split-field-label', true).createChild({
36649                 tag : 'i',
36650                 cls : 'text-danger fa fa-lg fa-star',
36651                 tooltip : 'This field is required',
36652                 style : 'margin-right:5px;'
36653             }, label, true);
36654         }
36655         
36656         this.fireEvent('invalid', this, msg);
36657     },
36658     
36659     clearInvalid : function()
36660     {
36661         var label = this.el.select('label', true).first();
36662         var icon = this.el.select('i.fa-star', true).first();
36663
36664         if(label && icon){
36665             icon.remove();
36666         }
36667         
36668         this.fireEvent('valid', this);
36669     },
36670     
36671     getName: function()
36672     {
36673         return this.name;
36674     }
36675     
36676 });
36677
36678  
36679
36680 /**
36681  * @class Roo.bootstrap.LayoutMasonry
36682  * @extends Roo.bootstrap.Component
36683  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
36684  * Bootstrap Layout Masonry class
36685  *
36686  * This is based on 
36687  * http://masonry.desandro.com
36688  *
36689  * The idea is to render all the bricks based on vertical width...
36690  *
36691  * The original code extends 'outlayer' - we might need to use that....
36692
36693  * @constructor
36694  * Create a new Element
36695  * @param {Object} config The config object
36696  */
36697
36698 Roo.bootstrap.LayoutMasonry = function(config){
36699     
36700     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
36701     
36702     this.bricks = [];
36703     
36704     Roo.bootstrap.LayoutMasonry.register(this);
36705     
36706     this.addEvents({
36707         // raw events
36708         /**
36709          * @event layout
36710          * Fire after layout the items
36711          * @param {Roo.bootstrap.LayoutMasonry} this
36712          * @param {Roo.EventObject} e
36713          */
36714         "layout" : true
36715     });
36716     
36717 };
36718
36719 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
36720     
36721     /**
36722      * @cfg {Boolean} isLayoutInstant = no animation?
36723      */   
36724     isLayoutInstant : false, // needed?
36725    
36726     /**
36727      * @cfg {Number} boxWidth  width of the columns
36728      */   
36729     boxWidth : 450,
36730     
36731       /**
36732      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
36733      */   
36734     boxHeight : 0,
36735     
36736     /**
36737      * @cfg {Number} padWidth padding below box..
36738      */   
36739     padWidth : 10, 
36740     
36741     /**
36742      * @cfg {Number} gutter gutter width..
36743      */   
36744     gutter : 10,
36745     
36746      /**
36747      * @cfg {Number} maxCols maximum number of columns
36748      */   
36749     
36750     maxCols: 0,
36751     
36752     /**
36753      * @cfg {Boolean} isAutoInitial defalut true
36754      */   
36755     isAutoInitial : true, 
36756     
36757     containerWidth: 0,
36758     
36759     /**
36760      * @cfg {Boolean} isHorizontal defalut false
36761      */   
36762     isHorizontal : false, 
36763
36764     currentSize : null,
36765     
36766     tag: 'div',
36767     
36768     cls: '',
36769     
36770     bricks: null, //CompositeElement
36771     
36772     cols : 1,
36773     
36774     _isLayoutInited : false,
36775     
36776 //    isAlternative : false, // only use for vertical layout...
36777     
36778     /**
36779      * @cfg {Number} alternativePadWidth padding below box..
36780      */   
36781     alternativePadWidth : 50,
36782     
36783     selectedBrick : [],
36784     
36785     getAutoCreate : function(){
36786         
36787         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
36788         
36789         var cfg = {
36790             tag: this.tag,
36791             cls: 'blog-masonary-wrapper ' + this.cls,
36792             cn : {
36793                 cls : 'mas-boxes masonary'
36794             }
36795         };
36796         
36797         return cfg;
36798     },
36799     
36800     getChildContainer: function( )
36801     {
36802         if (this.boxesEl) {
36803             return this.boxesEl;
36804         }
36805         
36806         this.boxesEl = this.el.select('.mas-boxes').first();
36807         
36808         return this.boxesEl;
36809     },
36810     
36811     
36812     initEvents : function()
36813     {
36814         var _this = this;
36815         
36816         if(this.isAutoInitial){
36817             Roo.log('hook children rendered');
36818             this.on('childrenrendered', function() {
36819                 Roo.log('children rendered');
36820                 _this.initial();
36821             } ,this);
36822         }
36823     },
36824     
36825     initial : function()
36826     {
36827         this.selectedBrick = [];
36828         
36829         this.currentSize = this.el.getBox(true);
36830         
36831         Roo.EventManager.onWindowResize(this.resize, this); 
36832
36833         if(!this.isAutoInitial){
36834             this.layout();
36835             return;
36836         }
36837         
36838         this.layout();
36839         
36840         return;
36841         //this.layout.defer(500,this);
36842         
36843     },
36844     
36845     resize : function()
36846     {
36847         var cs = this.el.getBox(true);
36848         
36849         if (
36850                 this.currentSize.width == cs.width && 
36851                 this.currentSize.x == cs.x && 
36852                 this.currentSize.height == cs.height && 
36853                 this.currentSize.y == cs.y 
36854         ) {
36855             Roo.log("no change in with or X or Y");
36856             return;
36857         }
36858         
36859         this.currentSize = cs;
36860         
36861         this.layout();
36862         
36863     },
36864     
36865     layout : function()
36866     {   
36867         this._resetLayout();
36868         
36869         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
36870         
36871         this.layoutItems( isInstant );
36872       
36873         this._isLayoutInited = true;
36874         
36875         this.fireEvent('layout', this);
36876         
36877     },
36878     
36879     _resetLayout : function()
36880     {
36881         if(this.isHorizontal){
36882             this.horizontalMeasureColumns();
36883             return;
36884         }
36885         
36886         this.verticalMeasureColumns();
36887         
36888     },
36889     
36890     verticalMeasureColumns : function()
36891     {
36892         this.getContainerWidth();
36893         
36894 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
36895 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
36896 //            return;
36897 //        }
36898         
36899         var boxWidth = this.boxWidth + this.padWidth;
36900         
36901         if(this.containerWidth < this.boxWidth){
36902             boxWidth = this.containerWidth
36903         }
36904         
36905         var containerWidth = this.containerWidth;
36906         
36907         var cols = Math.floor(containerWidth / boxWidth);
36908         
36909         this.cols = Math.max( cols, 1 );
36910         
36911         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36912         
36913         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
36914         
36915         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
36916         
36917         this.colWidth = boxWidth + avail - this.padWidth;
36918         
36919         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
36920         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
36921     },
36922     
36923     horizontalMeasureColumns : function()
36924     {
36925         this.getContainerWidth();
36926         
36927         var boxWidth = this.boxWidth;
36928         
36929         if(this.containerWidth < boxWidth){
36930             boxWidth = this.containerWidth;
36931         }
36932         
36933         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
36934         
36935         this.el.setHeight(boxWidth);
36936         
36937     },
36938     
36939     getContainerWidth : function()
36940     {
36941         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36942     },
36943     
36944     layoutItems : function( isInstant )
36945     {
36946         Roo.log(this.bricks);
36947         
36948         var items = Roo.apply([], this.bricks);
36949         
36950         if(this.isHorizontal){
36951             this._horizontalLayoutItems( items , isInstant );
36952             return;
36953         }
36954         
36955 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
36956 //            this._verticalAlternativeLayoutItems( items , isInstant );
36957 //            return;
36958 //        }
36959         
36960         this._verticalLayoutItems( items , isInstant );
36961         
36962     },
36963     
36964     _verticalLayoutItems : function ( items , isInstant)
36965     {
36966         if ( !items || !items.length ) {
36967             return;
36968         }
36969         
36970         var standard = [
36971             ['xs', 'xs', 'xs', 'tall'],
36972             ['xs', 'xs', 'tall'],
36973             ['xs', 'xs', 'sm'],
36974             ['xs', 'xs', 'xs'],
36975             ['xs', 'tall'],
36976             ['xs', 'sm'],
36977             ['xs', 'xs'],
36978             ['xs'],
36979             
36980             ['sm', 'xs', 'xs'],
36981             ['sm', 'xs'],
36982             ['sm'],
36983             
36984             ['tall', 'xs', 'xs', 'xs'],
36985             ['tall', 'xs', 'xs'],
36986             ['tall', 'xs'],
36987             ['tall']
36988             
36989         ];
36990         
36991         var queue = [];
36992         
36993         var boxes = [];
36994         
36995         var box = [];
36996         
36997         Roo.each(items, function(item, k){
36998             
36999             switch (item.size) {
37000                 // these layouts take up a full box,
37001                 case 'md' :
37002                 case 'md-left' :
37003                 case 'md-right' :
37004                 case 'wide' :
37005                     
37006                     if(box.length){
37007                         boxes.push(box);
37008                         box = [];
37009                     }
37010                     
37011                     boxes.push([item]);
37012                     
37013                     break;
37014                     
37015                 case 'xs' :
37016                 case 'sm' :
37017                 case 'tall' :
37018                     
37019                     box.push(item);
37020                     
37021                     break;
37022                 default :
37023                     break;
37024                     
37025             }
37026             
37027         }, this);
37028         
37029         if(box.length){
37030             boxes.push(box);
37031             box = [];
37032         }
37033         
37034         var filterPattern = function(box, length)
37035         {
37036             if(!box.length){
37037                 return;
37038             }
37039             
37040             var match = false;
37041             
37042             var pattern = box.slice(0, length);
37043             
37044             var format = [];
37045             
37046             Roo.each(pattern, function(i){
37047                 format.push(i.size);
37048             }, this);
37049             
37050             Roo.each(standard, function(s){
37051                 
37052                 if(String(s) != String(format)){
37053                     return;
37054                 }
37055                 
37056                 match = true;
37057                 return false;
37058                 
37059             }, this);
37060             
37061             if(!match && length == 1){
37062                 return;
37063             }
37064             
37065             if(!match){
37066                 filterPattern(box, length - 1);
37067                 return;
37068             }
37069                 
37070             queue.push(pattern);
37071
37072             box = box.slice(length, box.length);
37073
37074             filterPattern(box, 4);
37075
37076             return;
37077             
37078         }
37079         
37080         Roo.each(boxes, function(box, k){
37081             
37082             if(!box.length){
37083                 return;
37084             }
37085             
37086             if(box.length == 1){
37087                 queue.push(box);
37088                 return;
37089             }
37090             
37091             filterPattern(box, 4);
37092             
37093         }, this);
37094         
37095         this._processVerticalLayoutQueue( queue, isInstant );
37096         
37097     },
37098     
37099 //    _verticalAlternativeLayoutItems : function( items , isInstant )
37100 //    {
37101 //        if ( !items || !items.length ) {
37102 //            return;
37103 //        }
37104 //
37105 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
37106 //        
37107 //    },
37108     
37109     _horizontalLayoutItems : function ( items , isInstant)
37110     {
37111         if ( !items || !items.length || items.length < 3) {
37112             return;
37113         }
37114         
37115         items.reverse();
37116         
37117         var eItems = items.slice(0, 3);
37118         
37119         items = items.slice(3, items.length);
37120         
37121         var standard = [
37122             ['xs', 'xs', 'xs', 'wide'],
37123             ['xs', 'xs', 'wide'],
37124             ['xs', 'xs', 'sm'],
37125             ['xs', 'xs', 'xs'],
37126             ['xs', 'wide'],
37127             ['xs', 'sm'],
37128             ['xs', 'xs'],
37129             ['xs'],
37130             
37131             ['sm', 'xs', 'xs'],
37132             ['sm', 'xs'],
37133             ['sm'],
37134             
37135             ['wide', 'xs', 'xs', 'xs'],
37136             ['wide', 'xs', 'xs'],
37137             ['wide', 'xs'],
37138             ['wide'],
37139             
37140             ['wide-thin']
37141         ];
37142         
37143         var queue = [];
37144         
37145         var boxes = [];
37146         
37147         var box = [];
37148         
37149         Roo.each(items, function(item, k){
37150             
37151             switch (item.size) {
37152                 case 'md' :
37153                 case 'md-left' :
37154                 case 'md-right' :
37155                 case 'tall' :
37156                     
37157                     if(box.length){
37158                         boxes.push(box);
37159                         box = [];
37160                     }
37161                     
37162                     boxes.push([item]);
37163                     
37164                     break;
37165                     
37166                 case 'xs' :
37167                 case 'sm' :
37168                 case 'wide' :
37169                 case 'wide-thin' :
37170                     
37171                     box.push(item);
37172                     
37173                     break;
37174                 default :
37175                     break;
37176                     
37177             }
37178             
37179         }, this);
37180         
37181         if(box.length){
37182             boxes.push(box);
37183             box = [];
37184         }
37185         
37186         var filterPattern = function(box, length)
37187         {
37188             if(!box.length){
37189                 return;
37190             }
37191             
37192             var match = false;
37193             
37194             var pattern = box.slice(0, length);
37195             
37196             var format = [];
37197             
37198             Roo.each(pattern, function(i){
37199                 format.push(i.size);
37200             }, this);
37201             
37202             Roo.each(standard, function(s){
37203                 
37204                 if(String(s) != String(format)){
37205                     return;
37206                 }
37207                 
37208                 match = true;
37209                 return false;
37210                 
37211             }, this);
37212             
37213             if(!match && length == 1){
37214                 return;
37215             }
37216             
37217             if(!match){
37218                 filterPattern(box, length - 1);
37219                 return;
37220             }
37221                 
37222             queue.push(pattern);
37223
37224             box = box.slice(length, box.length);
37225
37226             filterPattern(box, 4);
37227
37228             return;
37229             
37230         }
37231         
37232         Roo.each(boxes, function(box, k){
37233             
37234             if(!box.length){
37235                 return;
37236             }
37237             
37238             if(box.length == 1){
37239                 queue.push(box);
37240                 return;
37241             }
37242             
37243             filterPattern(box, 4);
37244             
37245         }, this);
37246         
37247         
37248         var prune = [];
37249         
37250         var pos = this.el.getBox(true);
37251         
37252         var minX = pos.x;
37253         
37254         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37255         
37256         var hit_end = false;
37257         
37258         Roo.each(queue, function(box){
37259             
37260             if(hit_end){
37261                 
37262                 Roo.each(box, function(b){
37263                 
37264                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37265                     b.el.hide();
37266
37267                 }, this);
37268
37269                 return;
37270             }
37271             
37272             var mx = 0;
37273             
37274             Roo.each(box, function(b){
37275                 
37276                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37277                 b.el.show();
37278
37279                 mx = Math.max(mx, b.x);
37280                 
37281             }, this);
37282             
37283             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
37284             
37285             if(maxX < minX){
37286                 
37287                 Roo.each(box, function(b){
37288                 
37289                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37290                     b.el.hide();
37291                     
37292                 }, this);
37293                 
37294                 hit_end = true;
37295                 
37296                 return;
37297             }
37298             
37299             prune.push(box);
37300             
37301         }, this);
37302         
37303         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
37304     },
37305     
37306     /** Sets position of item in DOM
37307     * @param {Element} item
37308     * @param {Number} x - horizontal position
37309     * @param {Number} y - vertical position
37310     * @param {Boolean} isInstant - disables transitions
37311     */
37312     _processVerticalLayoutQueue : function( queue, isInstant )
37313     {
37314         var pos = this.el.getBox(true);
37315         var x = pos.x;
37316         var y = pos.y;
37317         var maxY = [];
37318         
37319         for (var i = 0; i < this.cols; i++){
37320             maxY[i] = pos.y;
37321         }
37322         
37323         Roo.each(queue, function(box, k){
37324             
37325             var col = k % this.cols;
37326             
37327             Roo.each(box, function(b,kk){
37328                 
37329                 b.el.position('absolute');
37330                 
37331                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37332                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37333                 
37334                 if(b.size == 'md-left' || b.size == 'md-right'){
37335                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37336                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37337                 }
37338                 
37339                 b.el.setWidth(width);
37340                 b.el.setHeight(height);
37341                 // iframe?
37342                 b.el.select('iframe',true).setSize(width,height);
37343                 
37344             }, this);
37345             
37346             for (var i = 0; i < this.cols; i++){
37347                 
37348                 if(maxY[i] < maxY[col]){
37349                     col = i;
37350                     continue;
37351                 }
37352                 
37353                 col = Math.min(col, i);
37354                 
37355             }
37356             
37357             x = pos.x + col * (this.colWidth + this.padWidth);
37358             
37359             y = maxY[col];
37360             
37361             var positions = [];
37362             
37363             switch (box.length){
37364                 case 1 :
37365                     positions = this.getVerticalOneBoxColPositions(x, y, box);
37366                     break;
37367                 case 2 :
37368                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
37369                     break;
37370                 case 3 :
37371                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
37372                     break;
37373                 case 4 :
37374                     positions = this.getVerticalFourBoxColPositions(x, y, box);
37375                     break;
37376                 default :
37377                     break;
37378             }
37379             
37380             Roo.each(box, function(b,kk){
37381                 
37382                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37383                 
37384                 var sz = b.el.getSize();
37385                 
37386                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
37387                 
37388             }, this);
37389             
37390         }, this);
37391         
37392         var mY = 0;
37393         
37394         for (var i = 0; i < this.cols; i++){
37395             mY = Math.max(mY, maxY[i]);
37396         }
37397         
37398         this.el.setHeight(mY - pos.y);
37399         
37400     },
37401     
37402 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
37403 //    {
37404 //        var pos = this.el.getBox(true);
37405 //        var x = pos.x;
37406 //        var y = pos.y;
37407 //        var maxX = pos.right;
37408 //        
37409 //        var maxHeight = 0;
37410 //        
37411 //        Roo.each(items, function(item, k){
37412 //            
37413 //            var c = k % 2;
37414 //            
37415 //            item.el.position('absolute');
37416 //                
37417 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
37418 //
37419 //            item.el.setWidth(width);
37420 //
37421 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
37422 //
37423 //            item.el.setHeight(height);
37424 //            
37425 //            if(c == 0){
37426 //                item.el.setXY([x, y], isInstant ? false : true);
37427 //            } else {
37428 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
37429 //            }
37430 //            
37431 //            y = y + height + this.alternativePadWidth;
37432 //            
37433 //            maxHeight = maxHeight + height + this.alternativePadWidth;
37434 //            
37435 //        }, this);
37436 //        
37437 //        this.el.setHeight(maxHeight);
37438 //        
37439 //    },
37440     
37441     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
37442     {
37443         var pos = this.el.getBox(true);
37444         
37445         var minX = pos.x;
37446         var minY = pos.y;
37447         
37448         var maxX = pos.right;
37449         
37450         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
37451         
37452         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37453         
37454         Roo.each(queue, function(box, k){
37455             
37456             Roo.each(box, function(b, kk){
37457                 
37458                 b.el.position('absolute');
37459                 
37460                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37461                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37462                 
37463                 if(b.size == 'md-left' || b.size == 'md-right'){
37464                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37465                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37466                 }
37467                 
37468                 b.el.setWidth(width);
37469                 b.el.setHeight(height);
37470                 
37471             }, this);
37472             
37473             if(!box.length){
37474                 return;
37475             }
37476             
37477             var positions = [];
37478             
37479             switch (box.length){
37480                 case 1 :
37481                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
37482                     break;
37483                 case 2 :
37484                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
37485                     break;
37486                 case 3 :
37487                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
37488                     break;
37489                 case 4 :
37490                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
37491                     break;
37492                 default :
37493                     break;
37494             }
37495             
37496             Roo.each(box, function(b,kk){
37497                 
37498                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37499                 
37500                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
37501                 
37502             }, this);
37503             
37504         }, this);
37505         
37506     },
37507     
37508     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
37509     {
37510         Roo.each(eItems, function(b,k){
37511             
37512             b.size = (k == 0) ? 'sm' : 'xs';
37513             b.x = (k == 0) ? 2 : 1;
37514             b.y = (k == 0) ? 2 : 1;
37515             
37516             b.el.position('absolute');
37517             
37518             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37519                 
37520             b.el.setWidth(width);
37521             
37522             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37523             
37524             b.el.setHeight(height);
37525             
37526         }, this);
37527
37528         var positions = [];
37529         
37530         positions.push({
37531             x : maxX - this.unitWidth * 2 - this.gutter,
37532             y : minY
37533         });
37534         
37535         positions.push({
37536             x : maxX - this.unitWidth,
37537             y : minY + (this.unitWidth + this.gutter) * 2
37538         });
37539         
37540         positions.push({
37541             x : maxX - this.unitWidth * 3 - this.gutter * 2,
37542             y : minY
37543         });
37544         
37545         Roo.each(eItems, function(b,k){
37546             
37547             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
37548
37549         }, this);
37550         
37551     },
37552     
37553     getVerticalOneBoxColPositions : function(x, y, box)
37554     {
37555         var pos = [];
37556         
37557         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
37558         
37559         if(box[0].size == 'md-left'){
37560             rand = 0;
37561         }
37562         
37563         if(box[0].size == 'md-right'){
37564             rand = 1;
37565         }
37566         
37567         pos.push({
37568             x : x + (this.unitWidth + this.gutter) * rand,
37569             y : y
37570         });
37571         
37572         return pos;
37573     },
37574     
37575     getVerticalTwoBoxColPositions : function(x, y, box)
37576     {
37577         var pos = [];
37578         
37579         if(box[0].size == 'xs'){
37580             
37581             pos.push({
37582                 x : x,
37583                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
37584             });
37585
37586             pos.push({
37587                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
37588                 y : y
37589             });
37590             
37591             return pos;
37592             
37593         }
37594         
37595         pos.push({
37596             x : x,
37597             y : y
37598         });
37599
37600         pos.push({
37601             x : x + (this.unitWidth + this.gutter) * 2,
37602             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
37603         });
37604         
37605         return pos;
37606         
37607     },
37608     
37609     getVerticalThreeBoxColPositions : function(x, y, box)
37610     {
37611         var pos = [];
37612         
37613         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37614             
37615             pos.push({
37616                 x : x,
37617                 y : y
37618             });
37619
37620             pos.push({
37621                 x : x + (this.unitWidth + this.gutter) * 1,
37622                 y : y
37623             });
37624             
37625             pos.push({
37626                 x : x + (this.unitWidth + this.gutter) * 2,
37627                 y : y
37628             });
37629             
37630             return pos;
37631             
37632         }
37633         
37634         if(box[0].size == 'xs' && box[1].size == 'xs'){
37635             
37636             pos.push({
37637                 x : x,
37638                 y : y
37639             });
37640
37641             pos.push({
37642                 x : x,
37643                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
37644             });
37645             
37646             pos.push({
37647                 x : x + (this.unitWidth + this.gutter) * 1,
37648                 y : y
37649             });
37650             
37651             return pos;
37652             
37653         }
37654         
37655         pos.push({
37656             x : x,
37657             y : y
37658         });
37659
37660         pos.push({
37661             x : x + (this.unitWidth + this.gutter) * 2,
37662             y : y
37663         });
37664
37665         pos.push({
37666             x : x + (this.unitWidth + this.gutter) * 2,
37667             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
37668         });
37669             
37670         return pos;
37671         
37672     },
37673     
37674     getVerticalFourBoxColPositions : function(x, y, box)
37675     {
37676         var pos = [];
37677         
37678         if(box[0].size == 'xs'){
37679             
37680             pos.push({
37681                 x : x,
37682                 y : y
37683             });
37684
37685             pos.push({
37686                 x : x,
37687                 y : y + (this.unitHeight + this.gutter) * 1
37688             });
37689             
37690             pos.push({
37691                 x : x,
37692                 y : y + (this.unitHeight + this.gutter) * 2
37693             });
37694             
37695             pos.push({
37696                 x : x + (this.unitWidth + this.gutter) * 1,
37697                 y : y
37698             });
37699             
37700             return pos;
37701             
37702         }
37703         
37704         pos.push({
37705             x : x,
37706             y : y
37707         });
37708
37709         pos.push({
37710             x : x + (this.unitWidth + this.gutter) * 2,
37711             y : y
37712         });
37713
37714         pos.push({
37715             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
37716             y : y + (this.unitHeight + this.gutter) * 1
37717         });
37718
37719         pos.push({
37720             x : x + (this.unitWidth + this.gutter) * 2,
37721             y : y + (this.unitWidth + this.gutter) * 2
37722         });
37723
37724         return pos;
37725         
37726     },
37727     
37728     getHorizontalOneBoxColPositions : function(maxX, minY, box)
37729     {
37730         var pos = [];
37731         
37732         if(box[0].size == 'md-left'){
37733             pos.push({
37734                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37735                 y : minY
37736             });
37737             
37738             return pos;
37739         }
37740         
37741         if(box[0].size == 'md-right'){
37742             pos.push({
37743                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37744                 y : minY + (this.unitWidth + this.gutter) * 1
37745             });
37746             
37747             return pos;
37748         }
37749         
37750         var rand = Math.floor(Math.random() * (4 - box[0].y));
37751         
37752         pos.push({
37753             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37754             y : minY + (this.unitWidth + this.gutter) * rand
37755         });
37756         
37757         return pos;
37758         
37759     },
37760     
37761     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
37762     {
37763         var pos = [];
37764         
37765         if(box[0].size == 'xs'){
37766             
37767             pos.push({
37768                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37769                 y : minY
37770             });
37771
37772             pos.push({
37773                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37774                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
37775             });
37776             
37777             return pos;
37778             
37779         }
37780         
37781         pos.push({
37782             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37783             y : minY
37784         });
37785
37786         pos.push({
37787             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37788             y : minY + (this.unitWidth + this.gutter) * 2
37789         });
37790         
37791         return pos;
37792         
37793     },
37794     
37795     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
37796     {
37797         var pos = [];
37798         
37799         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37800             
37801             pos.push({
37802                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37803                 y : minY
37804             });
37805
37806             pos.push({
37807                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37808                 y : minY + (this.unitWidth + this.gutter) * 1
37809             });
37810             
37811             pos.push({
37812                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37813                 y : minY + (this.unitWidth + this.gutter) * 2
37814             });
37815             
37816             return pos;
37817             
37818         }
37819         
37820         if(box[0].size == 'xs' && box[1].size == 'xs'){
37821             
37822             pos.push({
37823                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37824                 y : minY
37825             });
37826
37827             pos.push({
37828                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37829                 y : minY
37830             });
37831             
37832             pos.push({
37833                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37834                 y : minY + (this.unitWidth + this.gutter) * 1
37835             });
37836             
37837             return pos;
37838             
37839         }
37840         
37841         pos.push({
37842             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37843             y : minY
37844         });
37845
37846         pos.push({
37847             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37848             y : minY + (this.unitWidth + this.gutter) * 2
37849         });
37850
37851         pos.push({
37852             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37853             y : minY + (this.unitWidth + this.gutter) * 2
37854         });
37855             
37856         return pos;
37857         
37858     },
37859     
37860     getHorizontalFourBoxColPositions : function(maxX, minY, box)
37861     {
37862         var pos = [];
37863         
37864         if(box[0].size == 'xs'){
37865             
37866             pos.push({
37867                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37868                 y : minY
37869             });
37870
37871             pos.push({
37872                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37873                 y : minY
37874             });
37875             
37876             pos.push({
37877                 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),
37878                 y : minY
37879             });
37880             
37881             pos.push({
37882                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
37883                 y : minY + (this.unitWidth + this.gutter) * 1
37884             });
37885             
37886             return pos;
37887             
37888         }
37889         
37890         pos.push({
37891             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37892             y : minY
37893         });
37894         
37895         pos.push({
37896             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37897             y : minY + (this.unitWidth + this.gutter) * 2
37898         });
37899         
37900         pos.push({
37901             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37902             y : minY + (this.unitWidth + this.gutter) * 2
37903         });
37904         
37905         pos.push({
37906             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),
37907             y : minY + (this.unitWidth + this.gutter) * 2
37908         });
37909
37910         return pos;
37911         
37912     },
37913     
37914     /**
37915     * remove a Masonry Brick
37916     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
37917     */
37918     removeBrick : function(brick_id)
37919     {
37920         if (!brick_id) {
37921             return;
37922         }
37923         
37924         for (var i = 0; i<this.bricks.length; i++) {
37925             if (this.bricks[i].id == brick_id) {
37926                 this.bricks.splice(i,1);
37927                 this.el.dom.removeChild(Roo.get(brick_id).dom);
37928                 this.initial();
37929             }
37930         }
37931     },
37932     
37933     /**
37934     * adds a Masonry Brick
37935     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
37936     */
37937     addBrick : function(cfg)
37938     {
37939         var cn = new Roo.bootstrap.MasonryBrick(cfg);
37940         //this.register(cn);
37941         cn.parentId = this.id;
37942         cn.render(this.el);
37943         return cn;
37944     },
37945     
37946     /**
37947     * register a Masonry Brick
37948     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
37949     */
37950     
37951     register : function(brick)
37952     {
37953         this.bricks.push(brick);
37954         brick.masonryId = this.id;
37955     },
37956     
37957     /**
37958     * clear all the Masonry Brick
37959     */
37960     clearAll : function()
37961     {
37962         this.bricks = [];
37963         //this.getChildContainer().dom.innerHTML = "";
37964         this.el.dom.innerHTML = '';
37965     },
37966     
37967     getSelected : function()
37968     {
37969         if (!this.selectedBrick) {
37970             return false;
37971         }
37972         
37973         return this.selectedBrick;
37974     }
37975 });
37976
37977 Roo.apply(Roo.bootstrap.LayoutMasonry, {
37978     
37979     groups: {},
37980      /**
37981     * register a Masonry Layout
37982     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
37983     */
37984     
37985     register : function(layout)
37986     {
37987         this.groups[layout.id] = layout;
37988     },
37989     /**
37990     * fetch a  Masonry Layout based on the masonry layout ID
37991     * @param {string} the masonry layout to add
37992     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
37993     */
37994     
37995     get: function(layout_id) {
37996         if (typeof(this.groups[layout_id]) == 'undefined') {
37997             return false;
37998         }
37999         return this.groups[layout_id] ;
38000     }
38001     
38002     
38003     
38004 });
38005
38006  
38007
38008  /**
38009  *
38010  * This is based on 
38011  * http://masonry.desandro.com
38012  *
38013  * The idea is to render all the bricks based on vertical width...
38014  *
38015  * The original code extends 'outlayer' - we might need to use that....
38016  * 
38017  */
38018
38019
38020 /**
38021  * @class Roo.bootstrap.LayoutMasonryAuto
38022  * @extends Roo.bootstrap.Component
38023  * Bootstrap Layout Masonry class
38024  * 
38025  * @constructor
38026  * Create a new Element
38027  * @param {Object} config The config object
38028  */
38029
38030 Roo.bootstrap.LayoutMasonryAuto = function(config){
38031     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
38032 };
38033
38034 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
38035     
38036       /**
38037      * @cfg {Boolean} isFitWidth  - resize the width..
38038      */   
38039     isFitWidth : false,  // options..
38040     /**
38041      * @cfg {Boolean} isOriginLeft = left align?
38042      */   
38043     isOriginLeft : true,
38044     /**
38045      * @cfg {Boolean} isOriginTop = top align?
38046      */   
38047     isOriginTop : false,
38048     /**
38049      * @cfg {Boolean} isLayoutInstant = no animation?
38050      */   
38051     isLayoutInstant : false, // needed?
38052     /**
38053      * @cfg {Boolean} isResizingContainer = not sure if this is used..
38054      */   
38055     isResizingContainer : true,
38056     /**
38057      * @cfg {Number} columnWidth  width of the columns 
38058      */   
38059     
38060     columnWidth : 0,
38061     
38062     /**
38063      * @cfg {Number} maxCols maximum number of columns
38064      */   
38065     
38066     maxCols: 0,
38067     /**
38068      * @cfg {Number} padHeight padding below box..
38069      */   
38070     
38071     padHeight : 10, 
38072     
38073     /**
38074      * @cfg {Boolean} isAutoInitial defalut true
38075      */   
38076     
38077     isAutoInitial : true, 
38078     
38079     // private?
38080     gutter : 0,
38081     
38082     containerWidth: 0,
38083     initialColumnWidth : 0,
38084     currentSize : null,
38085     
38086     colYs : null, // array.
38087     maxY : 0,
38088     padWidth: 10,
38089     
38090     
38091     tag: 'div',
38092     cls: '',
38093     bricks: null, //CompositeElement
38094     cols : 0, // array?
38095     // element : null, // wrapped now this.el
38096     _isLayoutInited : null, 
38097     
38098     
38099     getAutoCreate : function(){
38100         
38101         var cfg = {
38102             tag: this.tag,
38103             cls: 'blog-masonary-wrapper ' + this.cls,
38104             cn : {
38105                 cls : 'mas-boxes masonary'
38106             }
38107         };
38108         
38109         return cfg;
38110     },
38111     
38112     getChildContainer: function( )
38113     {
38114         if (this.boxesEl) {
38115             return this.boxesEl;
38116         }
38117         
38118         this.boxesEl = this.el.select('.mas-boxes').first();
38119         
38120         return this.boxesEl;
38121     },
38122     
38123     
38124     initEvents : function()
38125     {
38126         var _this = this;
38127         
38128         if(this.isAutoInitial){
38129             Roo.log('hook children rendered');
38130             this.on('childrenrendered', function() {
38131                 Roo.log('children rendered');
38132                 _this.initial();
38133             } ,this);
38134         }
38135         
38136     },
38137     
38138     initial : function()
38139     {
38140         this.reloadItems();
38141
38142         this.currentSize = this.el.getBox(true);
38143
38144         /// was window resize... - let's see if this works..
38145         Roo.EventManager.onWindowResize(this.resize, this); 
38146
38147         if(!this.isAutoInitial){
38148             this.layout();
38149             return;
38150         }
38151         
38152         this.layout.defer(500,this);
38153     },
38154     
38155     reloadItems: function()
38156     {
38157         this.bricks = this.el.select('.masonry-brick', true);
38158         
38159         this.bricks.each(function(b) {
38160             //Roo.log(b.getSize());
38161             if (!b.attr('originalwidth')) {
38162                 b.attr('originalwidth',  b.getSize().width);
38163             }
38164             
38165         });
38166         
38167         Roo.log(this.bricks.elements.length);
38168     },
38169     
38170     resize : function()
38171     {
38172         Roo.log('resize');
38173         var cs = this.el.getBox(true);
38174         
38175         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
38176             Roo.log("no change in with or X");
38177             return;
38178         }
38179         this.currentSize = cs;
38180         this.layout();
38181     },
38182     
38183     layout : function()
38184     {
38185          Roo.log('layout');
38186         this._resetLayout();
38187         //this._manageStamps();
38188       
38189         // don't animate first layout
38190         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38191         this.layoutItems( isInstant );
38192       
38193         // flag for initalized
38194         this._isLayoutInited = true;
38195     },
38196     
38197     layoutItems : function( isInstant )
38198     {
38199         //var items = this._getItemsForLayout( this.items );
38200         // original code supports filtering layout items.. we just ignore it..
38201         
38202         this._layoutItems( this.bricks , isInstant );
38203       
38204         this._postLayout();
38205     },
38206     _layoutItems : function ( items , isInstant)
38207     {
38208        //this.fireEvent( 'layout', this, items );
38209     
38210
38211         if ( !items || !items.elements.length ) {
38212           // no items, emit event with empty array
38213             return;
38214         }
38215
38216         var queue = [];
38217         items.each(function(item) {
38218             Roo.log("layout item");
38219             Roo.log(item);
38220             // get x/y object from method
38221             var position = this._getItemLayoutPosition( item );
38222             // enqueue
38223             position.item = item;
38224             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
38225             queue.push( position );
38226         }, this);
38227       
38228         this._processLayoutQueue( queue );
38229     },
38230     /** Sets position of item in DOM
38231     * @param {Element} item
38232     * @param {Number} x - horizontal position
38233     * @param {Number} y - vertical position
38234     * @param {Boolean} isInstant - disables transitions
38235     */
38236     _processLayoutQueue : function( queue )
38237     {
38238         for ( var i=0, len = queue.length; i < len; i++ ) {
38239             var obj = queue[i];
38240             obj.item.position('absolute');
38241             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
38242         }
38243     },
38244       
38245     
38246     /**
38247     * Any logic you want to do after each layout,
38248     * i.e. size the container
38249     */
38250     _postLayout : function()
38251     {
38252         this.resizeContainer();
38253     },
38254     
38255     resizeContainer : function()
38256     {
38257         if ( !this.isResizingContainer ) {
38258             return;
38259         }
38260         var size = this._getContainerSize();
38261         if ( size ) {
38262             this.el.setSize(size.width,size.height);
38263             this.boxesEl.setSize(size.width,size.height);
38264         }
38265     },
38266     
38267     
38268     
38269     _resetLayout : function()
38270     {
38271         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
38272         this.colWidth = this.el.getWidth();
38273         //this.gutter = this.el.getWidth(); 
38274         
38275         this.measureColumns();
38276
38277         // reset column Y
38278         var i = this.cols;
38279         this.colYs = [];
38280         while (i--) {
38281             this.colYs.push( 0 );
38282         }
38283     
38284         this.maxY = 0;
38285     },
38286
38287     measureColumns : function()
38288     {
38289         this.getContainerWidth();
38290       // if columnWidth is 0, default to outerWidth of first item
38291         if ( !this.columnWidth ) {
38292             var firstItem = this.bricks.first();
38293             Roo.log(firstItem);
38294             this.columnWidth  = this.containerWidth;
38295             if (firstItem && firstItem.attr('originalwidth') ) {
38296                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
38297             }
38298             // columnWidth fall back to item of first element
38299             Roo.log("set column width?");
38300                         this.initialColumnWidth = this.columnWidth  ;
38301
38302             // if first elem has no width, default to size of container
38303             
38304         }
38305         
38306         
38307         if (this.initialColumnWidth) {
38308             this.columnWidth = this.initialColumnWidth;
38309         }
38310         
38311         
38312             
38313         // column width is fixed at the top - however if container width get's smaller we should
38314         // reduce it...
38315         
38316         // this bit calcs how man columns..
38317             
38318         var columnWidth = this.columnWidth += this.gutter;
38319       
38320         // calculate columns
38321         var containerWidth = this.containerWidth + this.gutter;
38322         
38323         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
38324         // fix rounding errors, typically with gutters
38325         var excess = columnWidth - containerWidth % columnWidth;
38326         
38327         
38328         // if overshoot is less than a pixel, round up, otherwise floor it
38329         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
38330         cols = Math[ mathMethod ]( cols );
38331         this.cols = Math.max( cols, 1 );
38332         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38333         
38334          // padding positioning..
38335         var totalColWidth = this.cols * this.columnWidth;
38336         var padavail = this.containerWidth - totalColWidth;
38337         // so for 2 columns - we need 3 'pads'
38338         
38339         var padNeeded = (1+this.cols) * this.padWidth;
38340         
38341         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
38342         
38343         this.columnWidth += padExtra
38344         //this.padWidth = Math.floor(padavail /  ( this.cols));
38345         
38346         // adjust colum width so that padding is fixed??
38347         
38348         // we have 3 columns ... total = width * 3
38349         // we have X left over... that should be used by 
38350         
38351         //if (this.expandC) {
38352             
38353         //}
38354         
38355         
38356         
38357     },
38358     
38359     getContainerWidth : function()
38360     {
38361        /* // container is parent if fit width
38362         var container = this.isFitWidth ? this.element.parentNode : this.element;
38363         // check that this.size and size are there
38364         // IE8 triggers resize on body size change, so they might not be
38365         
38366         var size = getSize( container );  //FIXME
38367         this.containerWidth = size && size.innerWidth; //FIXME
38368         */
38369          
38370         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38371         
38372     },
38373     
38374     _getItemLayoutPosition : function( item )  // what is item?
38375     {
38376         // we resize the item to our columnWidth..
38377       
38378         item.setWidth(this.columnWidth);
38379         item.autoBoxAdjust  = false;
38380         
38381         var sz = item.getSize();
38382  
38383         // how many columns does this brick span
38384         var remainder = this.containerWidth % this.columnWidth;
38385         
38386         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
38387         // round if off by 1 pixel, otherwise use ceil
38388         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
38389         colSpan = Math.min( colSpan, this.cols );
38390         
38391         // normally this should be '1' as we dont' currently allow multi width columns..
38392         
38393         var colGroup = this._getColGroup( colSpan );
38394         // get the minimum Y value from the columns
38395         var minimumY = Math.min.apply( Math, colGroup );
38396         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38397         
38398         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
38399          
38400         // position the brick
38401         var position = {
38402             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
38403             y: this.currentSize.y + minimumY + this.padHeight
38404         };
38405         
38406         Roo.log(position);
38407         // apply setHeight to necessary columns
38408         var setHeight = minimumY + sz.height + this.padHeight;
38409         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38410         
38411         var setSpan = this.cols + 1 - colGroup.length;
38412         for ( var i = 0; i < setSpan; i++ ) {
38413           this.colYs[ shortColIndex + i ] = setHeight ;
38414         }
38415       
38416         return position;
38417     },
38418     
38419     /**
38420      * @param {Number} colSpan - number of columns the element spans
38421      * @returns {Array} colGroup
38422      */
38423     _getColGroup : function( colSpan )
38424     {
38425         if ( colSpan < 2 ) {
38426           // if brick spans only one column, use all the column Ys
38427           return this.colYs;
38428         }
38429       
38430         var colGroup = [];
38431         // how many different places could this brick fit horizontally
38432         var groupCount = this.cols + 1 - colSpan;
38433         // for each group potential horizontal position
38434         for ( var i = 0; i < groupCount; i++ ) {
38435           // make an array of colY values for that one group
38436           var groupColYs = this.colYs.slice( i, i + colSpan );
38437           // and get the max value of the array
38438           colGroup[i] = Math.max.apply( Math, groupColYs );
38439         }
38440         return colGroup;
38441     },
38442     /*
38443     _manageStamp : function( stamp )
38444     {
38445         var stampSize =  stamp.getSize();
38446         var offset = stamp.getBox();
38447         // get the columns that this stamp affects
38448         var firstX = this.isOriginLeft ? offset.x : offset.right;
38449         var lastX = firstX + stampSize.width;
38450         var firstCol = Math.floor( firstX / this.columnWidth );
38451         firstCol = Math.max( 0, firstCol );
38452         
38453         var lastCol = Math.floor( lastX / this.columnWidth );
38454         // lastCol should not go over if multiple of columnWidth #425
38455         lastCol -= lastX % this.columnWidth ? 0 : 1;
38456         lastCol = Math.min( this.cols - 1, lastCol );
38457         
38458         // set colYs to bottom of the stamp
38459         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
38460             stampSize.height;
38461             
38462         for ( var i = firstCol; i <= lastCol; i++ ) {
38463           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
38464         }
38465     },
38466     */
38467     
38468     _getContainerSize : function()
38469     {
38470         this.maxY = Math.max.apply( Math, this.colYs );
38471         var size = {
38472             height: this.maxY
38473         };
38474       
38475         if ( this.isFitWidth ) {
38476             size.width = this._getContainerFitWidth();
38477         }
38478       
38479         return size;
38480     },
38481     
38482     _getContainerFitWidth : function()
38483     {
38484         var unusedCols = 0;
38485         // count unused columns
38486         var i = this.cols;
38487         while ( --i ) {
38488           if ( this.colYs[i] !== 0 ) {
38489             break;
38490           }
38491           unusedCols++;
38492         }
38493         // fit container to columns that have been used
38494         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
38495     },
38496     
38497     needsResizeLayout : function()
38498     {
38499         var previousWidth = this.containerWidth;
38500         this.getContainerWidth();
38501         return previousWidth !== this.containerWidth;
38502     }
38503  
38504 });
38505
38506  
38507
38508  /*
38509  * - LGPL
38510  *
38511  * element
38512  * 
38513  */
38514
38515 /**
38516  * @class Roo.bootstrap.MasonryBrick
38517  * @extends Roo.bootstrap.Component
38518  * Bootstrap MasonryBrick class
38519  * 
38520  * @constructor
38521  * Create a new MasonryBrick
38522  * @param {Object} config The config object
38523  */
38524
38525 Roo.bootstrap.MasonryBrick = function(config){
38526     
38527     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
38528     
38529     Roo.bootstrap.MasonryBrick.register(this);
38530     
38531     this.addEvents({
38532         // raw events
38533         /**
38534          * @event click
38535          * When a MasonryBrick is clcik
38536          * @param {Roo.bootstrap.MasonryBrick} this
38537          * @param {Roo.EventObject} e
38538          */
38539         "click" : true
38540     });
38541 };
38542
38543 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
38544     
38545     /**
38546      * @cfg {String} title
38547      */   
38548     title : '',
38549     /**
38550      * @cfg {String} html
38551      */   
38552     html : '',
38553     /**
38554      * @cfg {String} bgimage
38555      */   
38556     bgimage : '',
38557     /**
38558      * @cfg {String} videourl
38559      */   
38560     videourl : '',
38561     /**
38562      * @cfg {String} cls
38563      */   
38564     cls : '',
38565     /**
38566      * @cfg {String} href
38567      */   
38568     href : '',
38569     /**
38570      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
38571      */   
38572     size : 'xs',
38573     
38574     /**
38575      * @cfg {String} placetitle (center|bottom)
38576      */   
38577     placetitle : '',
38578     
38579     /**
38580      * @cfg {Boolean} isFitContainer defalut true
38581      */   
38582     isFitContainer : true, 
38583     
38584     /**
38585      * @cfg {Boolean} preventDefault defalut false
38586      */   
38587     preventDefault : false, 
38588     
38589     /**
38590      * @cfg {Boolean} inverse defalut false
38591      */   
38592     maskInverse : false, 
38593     
38594     getAutoCreate : function()
38595     {
38596         if(!this.isFitContainer){
38597             return this.getSplitAutoCreate();
38598         }
38599         
38600         var cls = 'masonry-brick masonry-brick-full';
38601         
38602         if(this.href.length){
38603             cls += ' masonry-brick-link';
38604         }
38605         
38606         if(this.bgimage.length){
38607             cls += ' masonry-brick-image';
38608         }
38609         
38610         if(this.maskInverse){
38611             cls += ' mask-inverse';
38612         }
38613         
38614         if(!this.html.length && !this.maskInverse && !this.videourl.length){
38615             cls += ' enable-mask';
38616         }
38617         
38618         if(this.size){
38619             cls += ' masonry-' + this.size + '-brick';
38620         }
38621         
38622         if(this.placetitle.length){
38623             
38624             switch (this.placetitle) {
38625                 case 'center' :
38626                     cls += ' masonry-center-title';
38627                     break;
38628                 case 'bottom' :
38629                     cls += ' masonry-bottom-title';
38630                     break;
38631                 default:
38632                     break;
38633             }
38634             
38635         } else {
38636             if(!this.html.length && !this.bgimage.length){
38637                 cls += ' masonry-center-title';
38638             }
38639
38640             if(!this.html.length && this.bgimage.length){
38641                 cls += ' masonry-bottom-title';
38642             }
38643         }
38644         
38645         if(this.cls){
38646             cls += ' ' + this.cls;
38647         }
38648         
38649         var cfg = {
38650             tag: (this.href.length) ? 'a' : 'div',
38651             cls: cls,
38652             cn: [
38653                 {
38654                     tag: 'div',
38655                     cls: 'masonry-brick-mask'
38656                 },
38657                 {
38658                     tag: 'div',
38659                     cls: 'masonry-brick-paragraph',
38660                     cn: []
38661                 }
38662             ]
38663         };
38664         
38665         if(this.href.length){
38666             cfg.href = this.href;
38667         }
38668         
38669         var cn = cfg.cn[1].cn;
38670         
38671         if(this.title.length){
38672             cn.push({
38673                 tag: 'h4',
38674                 cls: 'masonry-brick-title',
38675                 html: this.title
38676             });
38677         }
38678         
38679         if(this.html.length){
38680             cn.push({
38681                 tag: 'p',
38682                 cls: 'masonry-brick-text',
38683                 html: this.html
38684             });
38685         }
38686         
38687         if (!this.title.length && !this.html.length) {
38688             cfg.cn[1].cls += ' hide';
38689         }
38690         
38691         if(this.bgimage.length){
38692             cfg.cn.push({
38693                 tag: 'img',
38694                 cls: 'masonry-brick-image-view',
38695                 src: this.bgimage
38696             });
38697         }
38698         
38699         if(this.videourl.length){
38700             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38701             // youtube support only?
38702             cfg.cn.push({
38703                 tag: 'iframe',
38704                 cls: 'masonry-brick-image-view',
38705                 src: vurl,
38706                 frameborder : 0,
38707                 allowfullscreen : true
38708             });
38709         }
38710         
38711         return cfg;
38712         
38713     },
38714     
38715     getSplitAutoCreate : function()
38716     {
38717         var cls = 'masonry-brick masonry-brick-split';
38718         
38719         if(this.href.length){
38720             cls += ' masonry-brick-link';
38721         }
38722         
38723         if(this.bgimage.length){
38724             cls += ' masonry-brick-image';
38725         }
38726         
38727         if(this.size){
38728             cls += ' masonry-' + this.size + '-brick';
38729         }
38730         
38731         switch (this.placetitle) {
38732             case 'center' :
38733                 cls += ' masonry-center-title';
38734                 break;
38735             case 'bottom' :
38736                 cls += ' masonry-bottom-title';
38737                 break;
38738             default:
38739                 if(!this.bgimage.length){
38740                     cls += ' masonry-center-title';
38741                 }
38742
38743                 if(this.bgimage.length){
38744                     cls += ' masonry-bottom-title';
38745                 }
38746                 break;
38747         }
38748         
38749         if(this.cls){
38750             cls += ' ' + this.cls;
38751         }
38752         
38753         var cfg = {
38754             tag: (this.href.length) ? 'a' : 'div',
38755             cls: cls,
38756             cn: [
38757                 {
38758                     tag: 'div',
38759                     cls: 'masonry-brick-split-head',
38760                     cn: [
38761                         {
38762                             tag: 'div',
38763                             cls: 'masonry-brick-paragraph',
38764                             cn: []
38765                         }
38766                     ]
38767                 },
38768                 {
38769                     tag: 'div',
38770                     cls: 'masonry-brick-split-body',
38771                     cn: []
38772                 }
38773             ]
38774         };
38775         
38776         if(this.href.length){
38777             cfg.href = this.href;
38778         }
38779         
38780         if(this.title.length){
38781             cfg.cn[0].cn[0].cn.push({
38782                 tag: 'h4',
38783                 cls: 'masonry-brick-title',
38784                 html: this.title
38785             });
38786         }
38787         
38788         if(this.html.length){
38789             cfg.cn[1].cn.push({
38790                 tag: 'p',
38791                 cls: 'masonry-brick-text',
38792                 html: this.html
38793             });
38794         }
38795
38796         if(this.bgimage.length){
38797             cfg.cn[0].cn.push({
38798                 tag: 'img',
38799                 cls: 'masonry-brick-image-view',
38800                 src: this.bgimage
38801             });
38802         }
38803         
38804         if(this.videourl.length){
38805             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38806             // youtube support only?
38807             cfg.cn[0].cn.cn.push({
38808                 tag: 'iframe',
38809                 cls: 'masonry-brick-image-view',
38810                 src: vurl,
38811                 frameborder : 0,
38812                 allowfullscreen : true
38813             });
38814         }
38815         
38816         return cfg;
38817     },
38818     
38819     initEvents: function() 
38820     {
38821         switch (this.size) {
38822             case 'xs' :
38823                 this.x = 1;
38824                 this.y = 1;
38825                 break;
38826             case 'sm' :
38827                 this.x = 2;
38828                 this.y = 2;
38829                 break;
38830             case 'md' :
38831             case 'md-left' :
38832             case 'md-right' :
38833                 this.x = 3;
38834                 this.y = 3;
38835                 break;
38836             case 'tall' :
38837                 this.x = 2;
38838                 this.y = 3;
38839                 break;
38840             case 'wide' :
38841                 this.x = 3;
38842                 this.y = 2;
38843                 break;
38844             case 'wide-thin' :
38845                 this.x = 3;
38846                 this.y = 1;
38847                 break;
38848                         
38849             default :
38850                 break;
38851         }
38852         
38853         if(Roo.isTouch){
38854             this.el.on('touchstart', this.onTouchStart, this);
38855             this.el.on('touchmove', this.onTouchMove, this);
38856             this.el.on('touchend', this.onTouchEnd, this);
38857             this.el.on('contextmenu', this.onContextMenu, this);
38858         } else {
38859             this.el.on('mouseenter'  ,this.enter, this);
38860             this.el.on('mouseleave', this.leave, this);
38861             this.el.on('click', this.onClick, this);
38862         }
38863         
38864         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
38865             this.parent().bricks.push(this);   
38866         }
38867         
38868     },
38869     
38870     onClick: function(e, el)
38871     {
38872         var time = this.endTimer - this.startTimer;
38873         // Roo.log(e.preventDefault());
38874         if(Roo.isTouch){
38875             if(time > 1000){
38876                 e.preventDefault();
38877                 return;
38878             }
38879         }
38880         
38881         if(!this.preventDefault){
38882             return;
38883         }
38884         
38885         e.preventDefault();
38886         
38887         if (this.activeClass != '') {
38888             this.selectBrick();
38889         }
38890         
38891         this.fireEvent('click', this, e);
38892     },
38893     
38894     enter: function(e, el)
38895     {
38896         e.preventDefault();
38897         
38898         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
38899             return;
38900         }
38901         
38902         if(this.bgimage.length && this.html.length){
38903             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
38904         }
38905     },
38906     
38907     leave: function(e, el)
38908     {
38909         e.preventDefault();
38910         
38911         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
38912             return;
38913         }
38914         
38915         if(this.bgimage.length && this.html.length){
38916             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
38917         }
38918     },
38919     
38920     onTouchStart: function(e, el)
38921     {
38922 //        e.preventDefault();
38923         
38924         this.touchmoved = false;
38925         
38926         if(!this.isFitContainer){
38927             return;
38928         }
38929         
38930         if(!this.bgimage.length || !this.html.length){
38931             return;
38932         }
38933         
38934         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
38935         
38936         this.timer = new Date().getTime();
38937         
38938     },
38939     
38940     onTouchMove: function(e, el)
38941     {
38942         this.touchmoved = true;
38943     },
38944     
38945     onContextMenu : function(e,el)
38946     {
38947         e.preventDefault();
38948         e.stopPropagation();
38949         return false;
38950     },
38951     
38952     onTouchEnd: function(e, el)
38953     {
38954 //        e.preventDefault();
38955         
38956         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
38957         
38958             this.leave(e,el);
38959             
38960             return;
38961         }
38962         
38963         if(!this.bgimage.length || !this.html.length){
38964             
38965             if(this.href.length){
38966                 window.location.href = this.href;
38967             }
38968             
38969             return;
38970         }
38971         
38972         if(!this.isFitContainer){
38973             return;
38974         }
38975         
38976         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
38977         
38978         window.location.href = this.href;
38979     },
38980     
38981     //selection on single brick only
38982     selectBrick : function() {
38983         
38984         if (!this.parentId) {
38985             return;
38986         }
38987         
38988         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
38989         var index = m.selectedBrick.indexOf(this.id);
38990         
38991         if ( index > -1) {
38992             m.selectedBrick.splice(index,1);
38993             this.el.removeClass(this.activeClass);
38994             return;
38995         }
38996         
38997         for(var i = 0; i < m.selectedBrick.length; i++) {
38998             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
38999             b.el.removeClass(b.activeClass);
39000         }
39001         
39002         m.selectedBrick = [];
39003         
39004         m.selectedBrick.push(this.id);
39005         this.el.addClass(this.activeClass);
39006         return;
39007     },
39008     
39009     isSelected : function(){
39010         return this.el.hasClass(this.activeClass);
39011         
39012     }
39013 });
39014
39015 Roo.apply(Roo.bootstrap.MasonryBrick, {
39016     
39017     //groups: {},
39018     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
39019      /**
39020     * register a Masonry Brick
39021     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39022     */
39023     
39024     register : function(brick)
39025     {
39026         //this.groups[brick.id] = brick;
39027         this.groups.add(brick.id, brick);
39028     },
39029     /**
39030     * fetch a  masonry brick based on the masonry brick ID
39031     * @param {string} the masonry brick to add
39032     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
39033     */
39034     
39035     get: function(brick_id) 
39036     {
39037         // if (typeof(this.groups[brick_id]) == 'undefined') {
39038         //     return false;
39039         // }
39040         // return this.groups[brick_id] ;
39041         
39042         if(this.groups.key(brick_id)) {
39043             return this.groups.key(brick_id);
39044         }
39045         
39046         return false;
39047     }
39048     
39049     
39050     
39051 });
39052
39053  /*
39054  * - LGPL
39055  *
39056  * element
39057  * 
39058  */
39059
39060 /**
39061  * @class Roo.bootstrap.Brick
39062  * @extends Roo.bootstrap.Component
39063  * Bootstrap Brick class
39064  * 
39065  * @constructor
39066  * Create a new Brick
39067  * @param {Object} config The config object
39068  */
39069
39070 Roo.bootstrap.Brick = function(config){
39071     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
39072     
39073     this.addEvents({
39074         // raw events
39075         /**
39076          * @event click
39077          * When a Brick is click
39078          * @param {Roo.bootstrap.Brick} this
39079          * @param {Roo.EventObject} e
39080          */
39081         "click" : true
39082     });
39083 };
39084
39085 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
39086     
39087     /**
39088      * @cfg {String} title
39089      */   
39090     title : '',
39091     /**
39092      * @cfg {String} html
39093      */   
39094     html : '',
39095     /**
39096      * @cfg {String} bgimage
39097      */   
39098     bgimage : '',
39099     /**
39100      * @cfg {String} cls
39101      */   
39102     cls : '',
39103     /**
39104      * @cfg {String} href
39105      */   
39106     href : '',
39107     /**
39108      * @cfg {String} video
39109      */   
39110     video : '',
39111     /**
39112      * @cfg {Boolean} square
39113      */   
39114     square : true,
39115     
39116     getAutoCreate : function()
39117     {
39118         var cls = 'roo-brick';
39119         
39120         if(this.href.length){
39121             cls += ' roo-brick-link';
39122         }
39123         
39124         if(this.bgimage.length){
39125             cls += ' roo-brick-image';
39126         }
39127         
39128         if(!this.html.length && !this.bgimage.length){
39129             cls += ' roo-brick-center-title';
39130         }
39131         
39132         if(!this.html.length && this.bgimage.length){
39133             cls += ' roo-brick-bottom-title';
39134         }
39135         
39136         if(this.cls){
39137             cls += ' ' + this.cls;
39138         }
39139         
39140         var cfg = {
39141             tag: (this.href.length) ? 'a' : 'div',
39142             cls: cls,
39143             cn: [
39144                 {
39145                     tag: 'div',
39146                     cls: 'roo-brick-paragraph',
39147                     cn: []
39148                 }
39149             ]
39150         };
39151         
39152         if(this.href.length){
39153             cfg.href = this.href;
39154         }
39155         
39156         var cn = cfg.cn[0].cn;
39157         
39158         if(this.title.length){
39159             cn.push({
39160                 tag: 'h4',
39161                 cls: 'roo-brick-title',
39162                 html: this.title
39163             });
39164         }
39165         
39166         if(this.html.length){
39167             cn.push({
39168                 tag: 'p',
39169                 cls: 'roo-brick-text',
39170                 html: this.html
39171             });
39172         } else {
39173             cn.cls += ' hide';
39174         }
39175         
39176         if(this.bgimage.length){
39177             cfg.cn.push({
39178                 tag: 'img',
39179                 cls: 'roo-brick-image-view',
39180                 src: this.bgimage
39181             });
39182         }
39183         
39184         return cfg;
39185     },
39186     
39187     initEvents: function() 
39188     {
39189         if(this.title.length || this.html.length){
39190             this.el.on('mouseenter'  ,this.enter, this);
39191             this.el.on('mouseleave', this.leave, this);
39192         }
39193         
39194         Roo.EventManager.onWindowResize(this.resize, this); 
39195         
39196         if(this.bgimage.length){
39197             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
39198             this.imageEl.on('load', this.onImageLoad, this);
39199             return;
39200         }
39201         
39202         this.resize();
39203     },
39204     
39205     onImageLoad : function()
39206     {
39207         this.resize();
39208     },
39209     
39210     resize : function()
39211     {
39212         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
39213         
39214         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
39215         
39216         if(this.bgimage.length){
39217             var image = this.el.select('.roo-brick-image-view', true).first();
39218             
39219             image.setWidth(paragraph.getWidth());
39220             
39221             if(this.square){
39222                 image.setHeight(paragraph.getWidth());
39223             }
39224             
39225             this.el.setHeight(image.getHeight());
39226             paragraph.setHeight(image.getHeight());
39227             
39228         }
39229         
39230     },
39231     
39232     enter: function(e, el)
39233     {
39234         e.preventDefault();
39235         
39236         if(this.bgimage.length){
39237             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
39238             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
39239         }
39240     },
39241     
39242     leave: function(e, el)
39243     {
39244         e.preventDefault();
39245         
39246         if(this.bgimage.length){
39247             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
39248             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
39249         }
39250     }
39251     
39252 });
39253
39254  
39255
39256  /*
39257  * - LGPL
39258  *
39259  * Number field 
39260  */
39261
39262 /**
39263  * @class Roo.bootstrap.form.NumberField
39264  * @extends Roo.bootstrap.form.Input
39265  * Bootstrap NumberField class
39266  * 
39267  * 
39268  * 
39269  * 
39270  * @constructor
39271  * Create a new NumberField
39272  * @param {Object} config The config object
39273  */
39274
39275 Roo.bootstrap.form.NumberField = function(config){
39276     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
39277 };
39278
39279 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
39280     
39281     /**
39282      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39283      */
39284     allowDecimals : true,
39285     /**
39286      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39287      */
39288     decimalSeparator : ".",
39289     /**
39290      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39291      */
39292     decimalPrecision : 2,
39293     /**
39294      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39295      */
39296     allowNegative : true,
39297     
39298     /**
39299      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
39300      */
39301     allowZero: true,
39302     /**
39303      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39304      */
39305     minValue : Number.NEGATIVE_INFINITY,
39306     /**
39307      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39308      */
39309     maxValue : Number.MAX_VALUE,
39310     /**
39311      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39312      */
39313     minText : "The minimum value for this field is {0}",
39314     /**
39315      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39316      */
39317     maxText : "The maximum value for this field is {0}",
39318     /**
39319      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39320      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39321      */
39322     nanText : "{0} is not a valid number",
39323     /**
39324      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
39325      */
39326     thousandsDelimiter : false,
39327     /**
39328      * @cfg {String} valueAlign alignment of value
39329      */
39330     valueAlign : "left",
39331
39332     getAutoCreate : function()
39333     {
39334         var hiddenInput = {
39335             tag: 'input',
39336             type: 'hidden',
39337             id: Roo.id(),
39338             cls: 'hidden-number-input'
39339         };
39340         
39341         if (this.name) {
39342             hiddenInput.name = this.name;
39343         }
39344         
39345         this.name = '';
39346         
39347         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
39348         
39349         this.name = hiddenInput.name;
39350         
39351         if(cfg.cn.length > 0) {
39352             cfg.cn.push(hiddenInput);
39353         }
39354         
39355         return cfg;
39356     },
39357
39358     // private
39359     initEvents : function()
39360     {   
39361         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
39362         
39363         var allowed = "0123456789";
39364         
39365         if(this.allowDecimals){
39366             allowed += this.decimalSeparator;
39367         }
39368         
39369         if(this.allowNegative){
39370             allowed += "-";
39371         }
39372         
39373         if(this.thousandsDelimiter) {
39374             allowed += ",";
39375         }
39376         
39377         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39378         
39379         var keyPress = function(e){
39380             
39381             var k = e.getKey();
39382             
39383             var c = e.getCharCode();
39384             
39385             if(
39386                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39387                     allowed.indexOf(String.fromCharCode(c)) === -1
39388             ){
39389                 e.stopEvent();
39390                 return;
39391             }
39392             
39393             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39394                 return;
39395             }
39396             
39397             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39398                 e.stopEvent();
39399             }
39400         };
39401         
39402         this.el.on("keypress", keyPress, this);
39403     },
39404     
39405     validateValue : function(value)
39406     {
39407         
39408         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
39409             return false;
39410         }
39411         
39412         var num = this.parseValue(value);
39413         
39414         if(isNaN(num)){
39415             this.markInvalid(String.format(this.nanText, value));
39416             return false;
39417         }
39418         
39419         if(num < this.minValue){
39420             this.markInvalid(String.format(this.minText, this.minValue));
39421             return false;
39422         }
39423         
39424         if(num > this.maxValue){
39425             this.markInvalid(String.format(this.maxText, this.maxValue));
39426             return false;
39427         }
39428         
39429         return true;
39430     },
39431
39432     getValue : function()
39433     {
39434         var v = this.hiddenEl().getValue();
39435         
39436         return this.fixPrecision(this.parseValue(v));
39437     },
39438
39439     parseValue : function(value)
39440     {
39441         if(this.thousandsDelimiter) {
39442             value += "";
39443             r = new RegExp(",", "g");
39444             value = value.replace(r, "");
39445         }
39446         
39447         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39448         return isNaN(value) ? '' : value;
39449     },
39450
39451     fixPrecision : function(value)
39452     {
39453         if(this.thousandsDelimiter) {
39454             value += "";
39455             r = new RegExp(",", "g");
39456             value = value.replace(r, "");
39457         }
39458         
39459         var nan = isNaN(value);
39460         
39461         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
39462             return nan ? '' : value;
39463         }
39464         return parseFloat(value).toFixed(this.decimalPrecision);
39465     },
39466
39467     setValue : function(v)
39468     {
39469         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
39470         
39471         this.value = v;
39472         
39473         if(this.rendered){
39474             
39475             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39476             
39477             this.inputEl().dom.value = (v == '') ? '' :
39478                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
39479             
39480             if(!this.allowZero && v === '0') {
39481                 this.hiddenEl().dom.value = '';
39482                 this.inputEl().dom.value = '';
39483             }
39484             
39485             this.validate();
39486         }
39487     },
39488
39489     decimalPrecisionFcn : function(v)
39490     {
39491         return Math.floor(v);
39492     },
39493
39494     beforeBlur : function()
39495     {
39496         var v = this.parseValue(this.getRawValue());
39497         
39498         if(v || v === 0 || v === ''){
39499             this.setValue(v);
39500         }
39501     },
39502     
39503     hiddenEl : function()
39504     {
39505         return this.el.select('input.hidden-number-input',true).first();
39506     }
39507     
39508 });
39509
39510  
39511
39512 /*
39513 * Licence: LGPL
39514 */
39515
39516 /**
39517  * @class Roo.bootstrap.DocumentSlider
39518  * @extends Roo.bootstrap.Component
39519  * Bootstrap DocumentSlider class
39520  * 
39521  * @constructor
39522  * Create a new DocumentViewer
39523  * @param {Object} config The config object
39524  */
39525
39526 Roo.bootstrap.DocumentSlider = function(config){
39527     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
39528     
39529     this.files = [];
39530     
39531     this.addEvents({
39532         /**
39533          * @event initial
39534          * Fire after initEvent
39535          * @param {Roo.bootstrap.DocumentSlider} this
39536          */
39537         "initial" : true,
39538         /**
39539          * @event update
39540          * Fire after update
39541          * @param {Roo.bootstrap.DocumentSlider} this
39542          */
39543         "update" : true,
39544         /**
39545          * @event click
39546          * Fire after click
39547          * @param {Roo.bootstrap.DocumentSlider} this
39548          */
39549         "click" : true
39550     });
39551 };
39552
39553 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
39554     
39555     files : false,
39556     
39557     indicator : 0,
39558     
39559     getAutoCreate : function()
39560     {
39561         var cfg = {
39562             tag : 'div',
39563             cls : 'roo-document-slider',
39564             cn : [
39565                 {
39566                     tag : 'div',
39567                     cls : 'roo-document-slider-header',
39568                     cn : [
39569                         {
39570                             tag : 'div',
39571                             cls : 'roo-document-slider-header-title'
39572                         }
39573                     ]
39574                 },
39575                 {
39576                     tag : 'div',
39577                     cls : 'roo-document-slider-body',
39578                     cn : [
39579                         {
39580                             tag : 'div',
39581                             cls : 'roo-document-slider-prev',
39582                             cn : [
39583                                 {
39584                                     tag : 'i',
39585                                     cls : 'fa fa-chevron-left'
39586                                 }
39587                             ]
39588                         },
39589                         {
39590                             tag : 'div',
39591                             cls : 'roo-document-slider-thumb',
39592                             cn : [
39593                                 {
39594                                     tag : 'img',
39595                                     cls : 'roo-document-slider-image'
39596                                 }
39597                             ]
39598                         },
39599                         {
39600                             tag : 'div',
39601                             cls : 'roo-document-slider-next',
39602                             cn : [
39603                                 {
39604                                     tag : 'i',
39605                                     cls : 'fa fa-chevron-right'
39606                                 }
39607                             ]
39608                         }
39609                     ]
39610                 }
39611             ]
39612         };
39613         
39614         return cfg;
39615     },
39616     
39617     initEvents : function()
39618     {
39619         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
39620         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
39621         
39622         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
39623         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
39624         
39625         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
39626         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
39627         
39628         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
39629         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
39630         
39631         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
39632         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
39633         
39634         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
39635         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39636         
39637         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
39638         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39639         
39640         this.thumbEl.on('click', this.onClick, this);
39641         
39642         this.prevIndicator.on('click', this.prev, this);
39643         
39644         this.nextIndicator.on('click', this.next, this);
39645         
39646     },
39647     
39648     initial : function()
39649     {
39650         if(this.files.length){
39651             this.indicator = 1;
39652             this.update()
39653         }
39654         
39655         this.fireEvent('initial', this);
39656     },
39657     
39658     update : function()
39659     {
39660         this.imageEl.attr('src', this.files[this.indicator - 1]);
39661         
39662         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
39663         
39664         this.prevIndicator.show();
39665         
39666         if(this.indicator == 1){
39667             this.prevIndicator.hide();
39668         }
39669         
39670         this.nextIndicator.show();
39671         
39672         if(this.indicator == this.files.length){
39673             this.nextIndicator.hide();
39674         }
39675         
39676         this.thumbEl.scrollTo('top');
39677         
39678         this.fireEvent('update', this);
39679     },
39680     
39681     onClick : function(e)
39682     {
39683         e.preventDefault();
39684         
39685         this.fireEvent('click', this);
39686     },
39687     
39688     prev : function(e)
39689     {
39690         e.preventDefault();
39691         
39692         this.indicator = Math.max(1, this.indicator - 1);
39693         
39694         this.update();
39695     },
39696     
39697     next : function(e)
39698     {
39699         e.preventDefault();
39700         
39701         this.indicator = Math.min(this.files.length, this.indicator + 1);
39702         
39703         this.update();
39704     }
39705 });
39706 /*
39707  * - LGPL
39708  *
39709  * RadioSet
39710  *
39711  *
39712  */
39713
39714 /**
39715  * @class Roo.bootstrap.form.RadioSet
39716  * @extends Roo.bootstrap.form.Input
39717  * @children Roo.bootstrap.form.Radio
39718  * Bootstrap RadioSet class
39719  * @cfg {String} indicatorpos (left|right) default left
39720  * @cfg {Boolean} inline (true|false) inline the element (default true)
39721  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
39722  * @constructor
39723  * Create a new RadioSet
39724  * @param {Object} config The config object
39725  */
39726
39727 Roo.bootstrap.form.RadioSet = function(config){
39728     
39729     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
39730     
39731     this.radioes = [];
39732     
39733     Roo.bootstrap.form.RadioSet.register(this);
39734     
39735     this.addEvents({
39736         /**
39737         * @event check
39738         * Fires when the element is checked or unchecked.
39739         * @param {Roo.bootstrap.form.RadioSet} this This radio
39740         * @param {Roo.bootstrap.form.Radio} item The checked item
39741         */
39742        check : true,
39743        /**
39744         * @event click
39745         * Fires when the element is click.
39746         * @param {Roo.bootstrap.form.RadioSet} this This radio set
39747         * @param {Roo.bootstrap.form.Radio} item The checked item
39748         * @param {Roo.EventObject} e The event object
39749         */
39750        click : true
39751     });
39752     
39753 };
39754
39755 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
39756
39757     radioes : false,
39758     
39759     inline : true,
39760     
39761     weight : '',
39762     
39763     indicatorpos : 'left',
39764     
39765     getAutoCreate : function()
39766     {
39767         var label = {
39768             tag : 'label',
39769             cls : 'roo-radio-set-label',
39770             cn : [
39771                 {
39772                     tag : 'span',
39773                     html : this.fieldLabel
39774                 }
39775             ]
39776         };
39777         if (Roo.bootstrap.version == 3) {
39778             
39779             
39780             if(this.indicatorpos == 'left'){
39781                 label.cn.unshift({
39782                     tag : 'i',
39783                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
39784                     tooltip : 'This field is required'
39785                 });
39786             } else {
39787                 label.cn.push({
39788                     tag : 'i',
39789                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
39790                     tooltip : 'This field is required'
39791                 });
39792             }
39793         }
39794         var items = {
39795             tag : 'div',
39796             cls : 'roo-radio-set-items'
39797         };
39798         
39799         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
39800         
39801         if (align === 'left' && this.fieldLabel.length) {
39802             
39803             items = {
39804                 cls : "roo-radio-set-right", 
39805                 cn: [
39806                     items
39807                 ]
39808             };
39809             
39810             if(this.labelWidth > 12){
39811                 label.style = "width: " + this.labelWidth + 'px';
39812             }
39813             
39814             if(this.labelWidth < 13 && this.labelmd == 0){
39815                 this.labelmd = this.labelWidth;
39816             }
39817             
39818             if(this.labellg > 0){
39819                 label.cls += ' col-lg-' + this.labellg;
39820                 items.cls += ' col-lg-' + (12 - this.labellg);
39821             }
39822             
39823             if(this.labelmd > 0){
39824                 label.cls += ' col-md-' + this.labelmd;
39825                 items.cls += ' col-md-' + (12 - this.labelmd);
39826             }
39827             
39828             if(this.labelsm > 0){
39829                 label.cls += ' col-sm-' + this.labelsm;
39830                 items.cls += ' col-sm-' + (12 - this.labelsm);
39831             }
39832             
39833             if(this.labelxs > 0){
39834                 label.cls += ' col-xs-' + this.labelxs;
39835                 items.cls += ' col-xs-' + (12 - this.labelxs);
39836             }
39837         }
39838         
39839         var cfg = {
39840             tag : 'div',
39841             cls : 'roo-radio-set',
39842             cn : [
39843                 {
39844                     tag : 'input',
39845                     cls : 'roo-radio-set-input',
39846                     type : 'hidden',
39847                     name : this.name,
39848                     value : this.value ? this.value :  ''
39849                 },
39850                 label,
39851                 items
39852             ]
39853         };
39854         
39855         if(this.weight.length){
39856             cfg.cls += ' roo-radio-' + this.weight;
39857         }
39858         
39859         if(this.inline) {
39860             cfg.cls += ' roo-radio-set-inline';
39861         }
39862         
39863         var settings=this;
39864         ['xs','sm','md','lg'].map(function(size){
39865             if (settings[size]) {
39866                 cfg.cls += ' col-' + size + '-' + settings[size];
39867             }
39868         });
39869         
39870         return cfg;
39871         
39872     },
39873
39874     initEvents : function()
39875     {
39876         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
39877         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
39878         
39879         if(!this.fieldLabel.length){
39880             this.labelEl.hide();
39881         }
39882         
39883         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
39884         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
39885         
39886         this.indicator = this.indicatorEl();
39887         
39888         if(this.indicator){
39889             this.indicator.addClass('invisible');
39890         }
39891         
39892         this.originalValue = this.getValue();
39893         
39894     },
39895     
39896     inputEl: function ()
39897     {
39898         return this.el.select('.roo-radio-set-input', true).first();
39899     },
39900     
39901     getChildContainer : function()
39902     {
39903         return this.itemsEl;
39904     },
39905     
39906     register : function(item)
39907     {
39908         this.radioes.push(item);
39909         
39910     },
39911     
39912     validate : function()
39913     {   
39914         if(this.getVisibilityEl().hasClass('hidden')){
39915             return true;
39916         }
39917         
39918         var valid = false;
39919         
39920         Roo.each(this.radioes, function(i){
39921             if(!i.checked){
39922                 return;
39923             }
39924             
39925             valid = true;
39926             return false;
39927         });
39928         
39929         if(this.allowBlank) {
39930             return true;
39931         }
39932         
39933         if(this.disabled || valid){
39934             this.markValid();
39935             return true;
39936         }
39937         
39938         this.markInvalid();
39939         return false;
39940         
39941     },
39942     
39943     markValid : function()
39944     {
39945         if(this.labelEl.isVisible(true) && this.indicatorEl()){
39946             this.indicatorEl().removeClass('visible');
39947             this.indicatorEl().addClass('invisible');
39948         }
39949         
39950         
39951         if (Roo.bootstrap.version == 3) {
39952             this.el.removeClass([this.invalidClass, this.validClass]);
39953             this.el.addClass(this.validClass);
39954         } else {
39955             this.el.removeClass(['is-invalid','is-valid']);
39956             this.el.addClass(['is-valid']);
39957         }
39958         this.fireEvent('valid', this);
39959     },
39960     
39961     markInvalid : function(msg)
39962     {
39963         if(this.allowBlank || this.disabled){
39964             return;
39965         }
39966         
39967         if(this.labelEl.isVisible(true) && this.indicatorEl()){
39968             this.indicatorEl().removeClass('invisible');
39969             this.indicatorEl().addClass('visible');
39970         }
39971         if (Roo.bootstrap.version == 3) {
39972             this.el.removeClass([this.invalidClass, this.validClass]);
39973             this.el.addClass(this.invalidClass);
39974         } else {
39975             this.el.removeClass(['is-invalid','is-valid']);
39976             this.el.addClass(['is-invalid']);
39977         }
39978         
39979         this.fireEvent('invalid', this, msg);
39980         
39981     },
39982     
39983     setValue : function(v, suppressEvent)
39984     {   
39985         if(this.value === v){
39986             return;
39987         }
39988         
39989         this.value = v;
39990         
39991         if(this.rendered){
39992             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39993         }
39994         
39995         Roo.each(this.radioes, function(i){
39996             i.checked = false;
39997             i.el.removeClass('checked');
39998         });
39999         
40000         Roo.each(this.radioes, function(i){
40001             
40002             if(i.value === v || i.value.toString() === v.toString()){
40003                 i.checked = true;
40004                 i.el.addClass('checked');
40005                 
40006                 if(suppressEvent !== true){
40007                     this.fireEvent('check', this, i);
40008                 }
40009                 
40010                 return false;
40011             }
40012             
40013         }, this);
40014         
40015         this.validate();
40016     },
40017     
40018     clearInvalid : function(){
40019         
40020         if(!this.el || this.preventMark){
40021             return;
40022         }
40023         
40024         this.el.removeClass([this.invalidClass]);
40025         
40026         this.fireEvent('valid', this);
40027     }
40028     
40029 });
40030
40031 Roo.apply(Roo.bootstrap.form.RadioSet, {
40032     
40033     groups: {},
40034     
40035     register : function(set)
40036     {
40037         this.groups[set.name] = set;
40038     },
40039     
40040     get: function(name) 
40041     {
40042         if (typeof(this.groups[name]) == 'undefined') {
40043             return false;
40044         }
40045         
40046         return this.groups[name] ;
40047     }
40048     
40049 });
40050 /*
40051  * Based on:
40052  * Ext JS Library 1.1.1
40053  * Copyright(c) 2006-2007, Ext JS, LLC.
40054  *
40055  * Originally Released Under LGPL - original licence link has changed is not relivant.
40056  *
40057  * Fork - LGPL
40058  * <script type="text/javascript">
40059  */
40060
40061
40062 /**
40063  * @class Roo.bootstrap.SplitBar
40064  * @extends Roo.util.Observable
40065  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
40066  * <br><br>
40067  * Usage:
40068  * <pre><code>
40069 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
40070                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
40071 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
40072 split.minSize = 100;
40073 split.maxSize = 600;
40074 split.animate = true;
40075 split.on('moved', splitterMoved);
40076 </code></pre>
40077  * @constructor
40078  * Create a new SplitBar
40079  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
40080  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
40081  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40082  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
40083                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
40084                         position of the SplitBar).
40085  */
40086 Roo.bootstrap.SplitBar = function(cfg){
40087     
40088     /** @private */
40089     
40090     //{
40091     //  dragElement : elm
40092     //  resizingElement: el,
40093         // optional..
40094     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
40095     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
40096         // existingProxy ???
40097     //}
40098     
40099     this.el = Roo.get(cfg.dragElement, true);
40100     this.el.dom.unselectable = "on";
40101     /** @private */
40102     this.resizingEl = Roo.get(cfg.resizingElement, true);
40103
40104     /**
40105      * @private
40106      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40107      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
40108      * @type Number
40109      */
40110     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
40111     
40112     /**
40113      * The minimum size of the resizing element. (Defaults to 0)
40114      * @type Number
40115      */
40116     this.minSize = 0;
40117     
40118     /**
40119      * The maximum size of the resizing element. (Defaults to 2000)
40120      * @type Number
40121      */
40122     this.maxSize = 2000;
40123     
40124     /**
40125      * Whether to animate the transition to the new size
40126      * @type Boolean
40127      */
40128     this.animate = false;
40129     
40130     /**
40131      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
40132      * @type Boolean
40133      */
40134     this.useShim = false;
40135     
40136     /** @private */
40137     this.shim = null;
40138     
40139     if(!cfg.existingProxy){
40140         /** @private */
40141         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
40142     }else{
40143         this.proxy = Roo.get(cfg.existingProxy).dom;
40144     }
40145     /** @private */
40146     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
40147     
40148     /** @private */
40149     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
40150     
40151     /** @private */
40152     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
40153     
40154     /** @private */
40155     this.dragSpecs = {};
40156     
40157     /**
40158      * @private The adapter to use to positon and resize elements
40159      */
40160     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40161     this.adapter.init(this);
40162     
40163     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40164         /** @private */
40165         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
40166         this.el.addClass("roo-splitbar-h");
40167     }else{
40168         /** @private */
40169         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
40170         this.el.addClass("roo-splitbar-v");
40171     }
40172     
40173     this.addEvents({
40174         /**
40175          * @event resize
40176          * Fires when the splitter is moved (alias for {@link #event-moved})
40177          * @param {Roo.bootstrap.SplitBar} this
40178          * @param {Number} newSize the new width or height
40179          */
40180         "resize" : true,
40181         /**
40182          * @event moved
40183          * Fires when the splitter is moved
40184          * @param {Roo.bootstrap.SplitBar} this
40185          * @param {Number} newSize the new width or height
40186          */
40187         "moved" : true,
40188         /**
40189          * @event beforeresize
40190          * Fires before the splitter is dragged
40191          * @param {Roo.bootstrap.SplitBar} this
40192          */
40193         "beforeresize" : true,
40194
40195         "beforeapply" : true
40196     });
40197
40198     Roo.util.Observable.call(this);
40199 };
40200
40201 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
40202     onStartProxyDrag : function(x, y){
40203         this.fireEvent("beforeresize", this);
40204         if(!this.overlay){
40205             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
40206             o.unselectable();
40207             o.enableDisplayMode("block");
40208             // all splitbars share the same overlay
40209             Roo.bootstrap.SplitBar.prototype.overlay = o;
40210         }
40211         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
40212         this.overlay.show();
40213         Roo.get(this.proxy).setDisplayed("block");
40214         var size = this.adapter.getElementSize(this);
40215         this.activeMinSize = this.getMinimumSize();;
40216         this.activeMaxSize = this.getMaximumSize();;
40217         var c1 = size - this.activeMinSize;
40218         var c2 = Math.max(this.activeMaxSize - size, 0);
40219         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40220             this.dd.resetConstraints();
40221             this.dd.setXConstraint(
40222                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
40223                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
40224             );
40225             this.dd.setYConstraint(0, 0);
40226         }else{
40227             this.dd.resetConstraints();
40228             this.dd.setXConstraint(0, 0);
40229             this.dd.setYConstraint(
40230                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
40231                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
40232             );
40233          }
40234         this.dragSpecs.startSize = size;
40235         this.dragSpecs.startPoint = [x, y];
40236         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
40237     },
40238     
40239     /** 
40240      * @private Called after the drag operation by the DDProxy
40241      */
40242     onEndProxyDrag : function(e){
40243         Roo.get(this.proxy).setDisplayed(false);
40244         var endPoint = Roo.lib.Event.getXY(e);
40245         if(this.overlay){
40246             this.overlay.hide();
40247         }
40248         var newSize;
40249         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40250             newSize = this.dragSpecs.startSize + 
40251                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
40252                     endPoint[0] - this.dragSpecs.startPoint[0] :
40253                     this.dragSpecs.startPoint[0] - endPoint[0]
40254                 );
40255         }else{
40256             newSize = this.dragSpecs.startSize + 
40257                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
40258                     endPoint[1] - this.dragSpecs.startPoint[1] :
40259                     this.dragSpecs.startPoint[1] - endPoint[1]
40260                 );
40261         }
40262         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
40263         if(newSize != this.dragSpecs.startSize){
40264             if(this.fireEvent('beforeapply', this, newSize) !== false){
40265                 this.adapter.setElementSize(this, newSize);
40266                 this.fireEvent("moved", this, newSize);
40267                 this.fireEvent("resize", this, newSize);
40268             }
40269         }
40270     },
40271     
40272     /**
40273      * Get the adapter this SplitBar uses
40274      * @return The adapter object
40275      */
40276     getAdapter : function(){
40277         return this.adapter;
40278     },
40279     
40280     /**
40281      * Set the adapter this SplitBar uses
40282      * @param {Object} adapter A SplitBar adapter object
40283      */
40284     setAdapter : function(adapter){
40285         this.adapter = adapter;
40286         this.adapter.init(this);
40287     },
40288     
40289     /**
40290      * Gets the minimum size for the resizing element
40291      * @return {Number} The minimum size
40292      */
40293     getMinimumSize : function(){
40294         return this.minSize;
40295     },
40296     
40297     /**
40298      * Sets the minimum size for the resizing element
40299      * @param {Number} minSize The minimum size
40300      */
40301     setMinimumSize : function(minSize){
40302         this.minSize = minSize;
40303     },
40304     
40305     /**
40306      * Gets the maximum size for the resizing element
40307      * @return {Number} The maximum size
40308      */
40309     getMaximumSize : function(){
40310         return this.maxSize;
40311     },
40312     
40313     /**
40314      * Sets the maximum size for the resizing element
40315      * @param {Number} maxSize The maximum size
40316      */
40317     setMaximumSize : function(maxSize){
40318         this.maxSize = maxSize;
40319     },
40320     
40321     /**
40322      * Sets the initialize size for the resizing element
40323      * @param {Number} size The initial size
40324      */
40325     setCurrentSize : function(size){
40326         var oldAnimate = this.animate;
40327         this.animate = false;
40328         this.adapter.setElementSize(this, size);
40329         this.animate = oldAnimate;
40330     },
40331     
40332     /**
40333      * Destroy this splitbar. 
40334      * @param {Boolean} removeEl True to remove the element
40335      */
40336     destroy : function(removeEl){
40337         if(this.shim){
40338             this.shim.remove();
40339         }
40340         this.dd.unreg();
40341         this.proxy.parentNode.removeChild(this.proxy);
40342         if(removeEl){
40343             this.el.remove();
40344         }
40345     }
40346 });
40347
40348 /**
40349  * @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.
40350  */
40351 Roo.bootstrap.SplitBar.createProxy = function(dir){
40352     var proxy = new Roo.Element(document.createElement("div"));
40353     proxy.unselectable();
40354     var cls = 'roo-splitbar-proxy';
40355     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
40356     document.body.appendChild(proxy.dom);
40357     return proxy.dom;
40358 };
40359
40360 /** 
40361  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
40362  * Default Adapter. It assumes the splitter and resizing element are not positioned
40363  * elements and only gets/sets the width of the element. Generally used for table based layouts.
40364  */
40365 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
40366 };
40367
40368 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
40369     // do nothing for now
40370     init : function(s){
40371     
40372     },
40373     /**
40374      * Called before drag operations to get the current size of the resizing element. 
40375      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40376      */
40377      getElementSize : function(s){
40378         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40379             return s.resizingEl.getWidth();
40380         }else{
40381             return s.resizingEl.getHeight();
40382         }
40383     },
40384     
40385     /**
40386      * Called after drag operations to set the size of the resizing element.
40387      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40388      * @param {Number} newSize The new size to set
40389      * @param {Function} onComplete A function to be invoked when resizing is complete
40390      */
40391     setElementSize : function(s, newSize, onComplete){
40392         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40393             if(!s.animate){
40394                 s.resizingEl.setWidth(newSize);
40395                 if(onComplete){
40396                     onComplete(s, newSize);
40397                 }
40398             }else{
40399                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
40400             }
40401         }else{
40402             
40403             if(!s.animate){
40404                 s.resizingEl.setHeight(newSize);
40405                 if(onComplete){
40406                     onComplete(s, newSize);
40407                 }
40408             }else{
40409                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
40410             }
40411         }
40412     }
40413 };
40414
40415 /** 
40416  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
40417  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
40418  * Adapter that  moves the splitter element to align with the resized sizing element. 
40419  * Used with an absolute positioned SplitBar.
40420  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
40421  * document.body, make sure you assign an id to the body element.
40422  */
40423 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
40424     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40425     this.container = Roo.get(container);
40426 };
40427
40428 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
40429     init : function(s){
40430         this.basic.init(s);
40431     },
40432     
40433     getElementSize : function(s){
40434         return this.basic.getElementSize(s);
40435     },
40436     
40437     setElementSize : function(s, newSize, onComplete){
40438         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
40439     },
40440     
40441     moveSplitter : function(s){
40442         var yes = Roo.bootstrap.SplitBar;
40443         switch(s.placement){
40444             case yes.LEFT:
40445                 s.el.setX(s.resizingEl.getRight());
40446                 break;
40447             case yes.RIGHT:
40448                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
40449                 break;
40450             case yes.TOP:
40451                 s.el.setY(s.resizingEl.getBottom());
40452                 break;
40453             case yes.BOTTOM:
40454                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
40455                 break;
40456         }
40457     }
40458 };
40459
40460 /**
40461  * Orientation constant - Create a vertical SplitBar
40462  * @static
40463  * @type Number
40464  */
40465 Roo.bootstrap.SplitBar.VERTICAL = 1;
40466
40467 /**
40468  * Orientation constant - Create a horizontal SplitBar
40469  * @static
40470  * @type Number
40471  */
40472 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
40473
40474 /**
40475  * Placement constant - The resizing element is to the left of the splitter element
40476  * @static
40477  * @type Number
40478  */
40479 Roo.bootstrap.SplitBar.LEFT = 1;
40480
40481 /**
40482  * Placement constant - The resizing element is to the right of the splitter element
40483  * @static
40484  * @type Number
40485  */
40486 Roo.bootstrap.SplitBar.RIGHT = 2;
40487
40488 /**
40489  * Placement constant - The resizing element is positioned above the splitter element
40490  * @static
40491  * @type Number
40492  */
40493 Roo.bootstrap.SplitBar.TOP = 3;
40494
40495 /**
40496  * Placement constant - The resizing element is positioned under splitter element
40497  * @static
40498  * @type Number
40499  */
40500 Roo.bootstrap.SplitBar.BOTTOM = 4;
40501 /*
40502  * Based on:
40503  * Ext JS Library 1.1.1
40504  * Copyright(c) 2006-2007, Ext JS, LLC.
40505  *
40506  * Originally Released Under LGPL - original licence link has changed is not relivant.
40507  *
40508  * Fork - LGPL
40509  * <script type="text/javascript">
40510  */
40511
40512 /**
40513  * @class Roo.bootstrap.layout.Manager
40514  * @extends Roo.bootstrap.Component
40515  * @abstract
40516  * Base class for layout managers.
40517  */
40518 Roo.bootstrap.layout.Manager = function(config)
40519 {
40520     this.monitorWindowResize = true; // do this before we apply configuration.
40521     
40522     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
40523
40524
40525
40526
40527
40528     /** false to disable window resize monitoring @type Boolean */
40529     
40530     this.regions = {};
40531     this.addEvents({
40532         /**
40533          * @event layout
40534          * Fires when a layout is performed.
40535          * @param {Roo.LayoutManager} this
40536          */
40537         "layout" : true,
40538         /**
40539          * @event regionresized
40540          * Fires when the user resizes a region.
40541          * @param {Roo.LayoutRegion} region The resized region
40542          * @param {Number} newSize The new size (width for east/west, height for north/south)
40543          */
40544         "regionresized" : true,
40545         /**
40546          * @event regioncollapsed
40547          * Fires when a region is collapsed.
40548          * @param {Roo.LayoutRegion} region The collapsed region
40549          */
40550         "regioncollapsed" : true,
40551         /**
40552          * @event regionexpanded
40553          * Fires when a region is expanded.
40554          * @param {Roo.LayoutRegion} region The expanded region
40555          */
40556         "regionexpanded" : true
40557     });
40558     this.updating = false;
40559
40560     if (config.el) {
40561         this.el = Roo.get(config.el);
40562         this.initEvents();
40563     }
40564
40565 };
40566
40567 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
40568
40569
40570     regions : null,
40571
40572     monitorWindowResize : true,
40573
40574
40575     updating : false,
40576
40577
40578     onRender : function(ct, position)
40579     {
40580         if(!this.el){
40581             this.el = Roo.get(ct);
40582             this.initEvents();
40583         }
40584         //this.fireEvent('render',this);
40585     },
40586
40587
40588     initEvents: function()
40589     {
40590
40591
40592         // ie scrollbar fix
40593         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
40594             document.body.scroll = "no";
40595         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
40596             this.el.position('relative');
40597         }
40598         this.id = this.el.id;
40599         this.el.addClass("roo-layout-container");
40600         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
40601         if(this.el.dom != document.body ) {
40602             this.el.on('resize', this.layout,this);
40603             this.el.on('show', this.layout,this);
40604         }
40605
40606     },
40607
40608     /**
40609      * Returns true if this layout is currently being updated
40610      * @return {Boolean}
40611      */
40612     isUpdating : function(){
40613         return this.updating;
40614     },
40615
40616     /**
40617      * Suspend the LayoutManager from doing auto-layouts while
40618      * making multiple add or remove calls
40619      */
40620     beginUpdate : function(){
40621         this.updating = true;
40622     },
40623
40624     /**
40625      * Restore auto-layouts and optionally disable the manager from performing a layout
40626      * @param {Boolean} noLayout true to disable a layout update
40627      */
40628     endUpdate : function(noLayout){
40629         this.updating = false;
40630         if(!noLayout){
40631             this.layout();
40632         }
40633     },
40634
40635     layout: function(){
40636         // abstract...
40637     },
40638
40639     onRegionResized : function(region, newSize){
40640         this.fireEvent("regionresized", region, newSize);
40641         this.layout();
40642     },
40643
40644     onRegionCollapsed : function(region){
40645         this.fireEvent("regioncollapsed", region);
40646     },
40647
40648     onRegionExpanded : function(region){
40649         this.fireEvent("regionexpanded", region);
40650     },
40651
40652     /**
40653      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
40654      * performs box-model adjustments.
40655      * @return {Object} The size as an object {width: (the width), height: (the height)}
40656      */
40657     getViewSize : function()
40658     {
40659         var size;
40660         if(this.el.dom != document.body){
40661             size = this.el.getSize();
40662         }else{
40663             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
40664         }
40665         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
40666         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40667         return size;
40668     },
40669
40670     /**
40671      * Returns the Element this layout is bound to.
40672      * @return {Roo.Element}
40673      */
40674     getEl : function(){
40675         return this.el;
40676     },
40677
40678     /**
40679      * Returns the specified region.
40680      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
40681      * @return {Roo.LayoutRegion}
40682      */
40683     getRegion : function(target){
40684         return this.regions[target.toLowerCase()];
40685     },
40686
40687     onWindowResize : function(){
40688         if(this.monitorWindowResize){
40689             this.layout();
40690         }
40691     }
40692 });
40693 /*
40694  * Based on:
40695  * Ext JS Library 1.1.1
40696  * Copyright(c) 2006-2007, Ext JS, LLC.
40697  *
40698  * Originally Released Under LGPL - original licence link has changed is not relivant.
40699  *
40700  * Fork - LGPL
40701  * <script type="text/javascript">
40702  */
40703 /**
40704  * @class Roo.bootstrap.layout.Border
40705  * @extends Roo.bootstrap.layout.Manager
40706  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
40707  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
40708  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
40709  * please see: examples/bootstrap/nested.html<br><br>
40710  
40711 <b>The container the layout is rendered into can be either the body element or any other element.
40712 If it is not the body element, the container needs to either be an absolute positioned element,
40713 or you will need to add "position:relative" to the css of the container.  You will also need to specify
40714 the container size if it is not the body element.</b>
40715
40716 * @constructor
40717 * Create a new Border
40718 * @param {Object} config Configuration options
40719  */
40720 Roo.bootstrap.layout.Border = function(config){
40721     config = config || {};
40722     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
40723     
40724     
40725     
40726     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40727         if(config[region]){
40728             config[region].region = region;
40729             this.addRegion(config[region]);
40730         }
40731     },this);
40732     
40733 };
40734
40735 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
40736
40737 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
40738     
40739         /**
40740          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
40741          */
40742         /**
40743          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
40744          */
40745         /**
40746          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
40747          */
40748         /**
40749          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
40750          */
40751         /**
40752          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
40753          */
40754         
40755         
40756         
40757         
40758     parent : false, // this might point to a 'nest' or a ???
40759     
40760     /**
40761      * Creates and adds a new region if it doesn't already exist.
40762      * @param {String} target The target region key (north, south, east, west or center).
40763      * @param {Object} config The regions config object
40764      * @return {BorderLayoutRegion} The new region
40765      */
40766     addRegion : function(config)
40767     {
40768         if(!this.regions[config.region]){
40769             var r = this.factory(config);
40770             this.bindRegion(r);
40771         }
40772         return this.regions[config.region];
40773     },
40774
40775     // private (kinda)
40776     bindRegion : function(r){
40777         this.regions[r.config.region] = r;
40778         
40779         r.on("visibilitychange",    this.layout, this);
40780         r.on("paneladded",          this.layout, this);
40781         r.on("panelremoved",        this.layout, this);
40782         r.on("invalidated",         this.layout, this);
40783         r.on("resized",             this.onRegionResized, this);
40784         r.on("collapsed",           this.onRegionCollapsed, this);
40785         r.on("expanded",            this.onRegionExpanded, this);
40786     },
40787
40788     /**
40789      * Performs a layout update.
40790      */
40791     layout : function()
40792     {
40793         if(this.updating) {
40794             return;
40795         }
40796         
40797         // render all the rebions if they have not been done alreayd?
40798         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40799             if(this.regions[region] && !this.regions[region].bodyEl){
40800                 this.regions[region].onRender(this.el)
40801             }
40802         },this);
40803         
40804         var size = this.getViewSize();
40805         var w = size.width;
40806         var h = size.height;
40807         var centerW = w;
40808         var centerH = h;
40809         var centerY = 0;
40810         var centerX = 0;
40811         //var x = 0, y = 0;
40812
40813         var rs = this.regions;
40814         var north = rs["north"];
40815         var south = rs["south"]; 
40816         var west = rs["west"];
40817         var east = rs["east"];
40818         var center = rs["center"];
40819         //if(this.hideOnLayout){ // not supported anymore
40820             //c.el.setStyle("display", "none");
40821         //}
40822         if(north && north.isVisible()){
40823             var b = north.getBox();
40824             var m = north.getMargins();
40825             b.width = w - (m.left+m.right);
40826             b.x = m.left;
40827             b.y = m.top;
40828             centerY = b.height + b.y + m.bottom;
40829             centerH -= centerY;
40830             north.updateBox(this.safeBox(b));
40831         }
40832         if(south && south.isVisible()){
40833             var b = south.getBox();
40834             var m = south.getMargins();
40835             b.width = w - (m.left+m.right);
40836             b.x = m.left;
40837             var totalHeight = (b.height + m.top + m.bottom);
40838             b.y = h - totalHeight + m.top;
40839             centerH -= totalHeight;
40840             south.updateBox(this.safeBox(b));
40841         }
40842         if(west && west.isVisible()){
40843             var b = west.getBox();
40844             var m = west.getMargins();
40845             b.height = centerH - (m.top+m.bottom);
40846             b.x = m.left;
40847             b.y = centerY + m.top;
40848             var totalWidth = (b.width + m.left + m.right);
40849             centerX += totalWidth;
40850             centerW -= totalWidth;
40851             west.updateBox(this.safeBox(b));
40852         }
40853         if(east && east.isVisible()){
40854             var b = east.getBox();
40855             var m = east.getMargins();
40856             b.height = centerH - (m.top+m.bottom);
40857             var totalWidth = (b.width + m.left + m.right);
40858             b.x = w - totalWidth + m.left;
40859             b.y = centerY + m.top;
40860             centerW -= totalWidth;
40861             east.updateBox(this.safeBox(b));
40862         }
40863         if(center){
40864             var m = center.getMargins();
40865             var centerBox = {
40866                 x: centerX + m.left,
40867                 y: centerY + m.top,
40868                 width: centerW - (m.left+m.right),
40869                 height: centerH - (m.top+m.bottom)
40870             };
40871             //if(this.hideOnLayout){
40872                 //center.el.setStyle("display", "block");
40873             //}
40874             center.updateBox(this.safeBox(centerBox));
40875         }
40876         this.el.repaint();
40877         this.fireEvent("layout", this);
40878     },
40879
40880     // private
40881     safeBox : function(box){
40882         box.width = Math.max(0, box.width);
40883         box.height = Math.max(0, box.height);
40884         return box;
40885     },
40886
40887     /**
40888      * Adds a ContentPanel (or subclass) to this layout.
40889      * @param {String} target The target region key (north, south, east, west or center).
40890      * @param {Roo.ContentPanel} panel The panel to add
40891      * @return {Roo.ContentPanel} The added panel
40892      */
40893     add : function(target, panel){
40894          
40895         target = target.toLowerCase();
40896         return this.regions[target].add(panel);
40897     },
40898
40899     /**
40900      * Remove a ContentPanel (or subclass) to this layout.
40901      * @param {String} target The target region key (north, south, east, west or center).
40902      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
40903      * @return {Roo.ContentPanel} The removed panel
40904      */
40905     remove : function(target, panel){
40906         target = target.toLowerCase();
40907         return this.regions[target].remove(panel);
40908     },
40909
40910     /**
40911      * Searches all regions for a panel with the specified id
40912      * @param {String} panelId
40913      * @return {Roo.ContentPanel} The panel or null if it wasn't found
40914      */
40915     findPanel : function(panelId){
40916         var rs = this.regions;
40917         for(var target in rs){
40918             if(typeof rs[target] != "function"){
40919                 var p = rs[target].getPanel(panelId);
40920                 if(p){
40921                     return p;
40922                 }
40923             }
40924         }
40925         return null;
40926     },
40927
40928     /**
40929      * Searches all regions for a panel with the specified id and activates (shows) it.
40930      * @param {String/ContentPanel} panelId The panels id or the panel itself
40931      * @return {Roo.ContentPanel} The shown panel or null
40932      */
40933     showPanel : function(panelId) {
40934       var rs = this.regions;
40935       for(var target in rs){
40936          var r = rs[target];
40937          if(typeof r != "function"){
40938             if(r.hasPanel(panelId)){
40939                return r.showPanel(panelId);
40940             }
40941          }
40942       }
40943       return null;
40944    },
40945
40946    /**
40947      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
40948      * @param {Roo.state.Provider} provider (optional) An alternate state provider
40949      */
40950    /*
40951     restoreState : function(provider){
40952         if(!provider){
40953             provider = Roo.state.Manager;
40954         }
40955         var sm = new Roo.LayoutStateManager();
40956         sm.init(this, provider);
40957     },
40958 */
40959  
40960  
40961     /**
40962      * Adds a xtype elements to the layout.
40963      * <pre><code>
40964
40965 layout.addxtype({
40966        xtype : 'ContentPanel',
40967        region: 'west',
40968        items: [ .... ]
40969    }
40970 );
40971
40972 layout.addxtype({
40973         xtype : 'NestedLayoutPanel',
40974         region: 'west',
40975         layout: {
40976            center: { },
40977            west: { }   
40978         },
40979         items : [ ... list of content panels or nested layout panels.. ]
40980    }
40981 );
40982 </code></pre>
40983      * @param {Object} cfg Xtype definition of item to add.
40984      */
40985     addxtype : function(cfg)
40986     {
40987         // basically accepts a pannel...
40988         // can accept a layout region..!?!?
40989         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
40990         
40991         
40992         // theory?  children can only be panels??
40993         
40994         //if (!cfg.xtype.match(/Panel$/)) {
40995         //    return false;
40996         //}
40997         var ret = false;
40998         
40999         if (typeof(cfg.region) == 'undefined') {
41000             Roo.log("Failed to add Panel, region was not set");
41001             Roo.log(cfg);
41002             return false;
41003         }
41004         var region = cfg.region;
41005         delete cfg.region;
41006         
41007           
41008         var xitems = [];
41009         if (cfg.items) {
41010             xitems = cfg.items;
41011             delete cfg.items;
41012         }
41013         var nb = false;
41014         
41015         if ( region == 'center') {
41016             Roo.log("Center: " + cfg.title);
41017         }
41018         
41019         
41020         switch(cfg.xtype) 
41021         {
41022             case 'Content':  // ContentPanel (el, cfg)
41023             case 'Scroll':  // ContentPanel (el, cfg)
41024             case 'View': 
41025                 cfg.autoCreate = cfg.autoCreate || true;
41026                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41027                 //} else {
41028                 //    var el = this.el.createChild();
41029                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
41030                 //}
41031                 
41032                 this.add(region, ret);
41033                 break;
41034             
41035             /*
41036             case 'TreePanel': // our new panel!
41037                 cfg.el = this.el.createChild();
41038                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41039                 this.add(region, ret);
41040                 break;
41041             */
41042             
41043             case 'Nest': 
41044                 // create a new Layout (which is  a Border Layout...
41045                 
41046                 var clayout = cfg.layout;
41047                 clayout.el  = this.el.createChild();
41048                 clayout.items   = clayout.items  || [];
41049                 
41050                 delete cfg.layout;
41051                 
41052                 // replace this exitems with the clayout ones..
41053                 xitems = clayout.items;
41054                  
41055                 // force background off if it's in center...
41056                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
41057                     cfg.background = false;
41058                 }
41059                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
41060                 
41061                 
41062                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41063                 //console.log('adding nested layout panel '  + cfg.toSource());
41064                 this.add(region, ret);
41065                 nb = {}; /// find first...
41066                 break;
41067             
41068             case 'Grid':
41069                 
41070                 // needs grid and region
41071                 
41072                 //var el = this.getRegion(region).el.createChild();
41073                 /*
41074                  *var el = this.el.createChild();
41075                 // create the grid first...
41076                 cfg.grid.container = el;
41077                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
41078                 */
41079                 
41080                 if (region == 'center' && this.active ) {
41081                     cfg.background = false;
41082                 }
41083                 
41084                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41085                 
41086                 this.add(region, ret);
41087                 /*
41088                 if (cfg.background) {
41089                     // render grid on panel activation (if panel background)
41090                     ret.on('activate', function(gp) {
41091                         if (!gp.grid.rendered) {
41092                     //        gp.grid.render(el);
41093                         }
41094                     });
41095                 } else {
41096                   //  cfg.grid.render(el);
41097                 }
41098                 */
41099                 break;
41100            
41101            
41102             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
41103                 // it was the old xcomponent building that caused this before.
41104                 // espeically if border is the top element in the tree.
41105                 ret = this;
41106                 break; 
41107                 
41108                     
41109                 
41110                 
41111                 
41112             default:
41113                 /*
41114                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
41115                     
41116                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41117                     this.add(region, ret);
41118                 } else {
41119                 */
41120                     Roo.log(cfg);
41121                     throw "Can not add '" + cfg.xtype + "' to Border";
41122                     return null;
41123              
41124                                 
41125              
41126         }
41127         this.beginUpdate();
41128         // add children..
41129         var region = '';
41130         var abn = {};
41131         Roo.each(xitems, function(i)  {
41132             region = nb && i.region ? i.region : false;
41133             
41134             var add = ret.addxtype(i);
41135            
41136             if (region) {
41137                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
41138                 if (!i.background) {
41139                     abn[region] = nb[region] ;
41140                 }
41141             }
41142             
41143         });
41144         this.endUpdate();
41145
41146         // make the last non-background panel active..
41147         //if (nb) { Roo.log(abn); }
41148         if (nb) {
41149             
41150             for(var r in abn) {
41151                 region = this.getRegion(r);
41152                 if (region) {
41153                     // tried using nb[r], but it does not work..
41154                      
41155                     region.showPanel(abn[r]);
41156                    
41157                 }
41158             }
41159         }
41160         return ret;
41161         
41162     },
41163     
41164     
41165 // private
41166     factory : function(cfg)
41167     {
41168         
41169         var validRegions = Roo.bootstrap.layout.Border.regions;
41170
41171         var target = cfg.region;
41172         cfg.mgr = this;
41173         
41174         var r = Roo.bootstrap.layout;
41175         Roo.log(target);
41176         switch(target){
41177             case "north":
41178                 return new r.North(cfg);
41179             case "south":
41180                 return new r.South(cfg);
41181             case "east":
41182                 return new r.East(cfg);
41183             case "west":
41184                 return new r.West(cfg);
41185             case "center":
41186                 return new r.Center(cfg);
41187         }
41188         throw 'Layout region "'+target+'" not supported.';
41189     }
41190     
41191     
41192 });
41193  /*
41194  * Based on:
41195  * Ext JS Library 1.1.1
41196  * Copyright(c) 2006-2007, Ext JS, LLC.
41197  *
41198  * Originally Released Under LGPL - original licence link has changed is not relivant.
41199  *
41200  * Fork - LGPL
41201  * <script type="text/javascript">
41202  */
41203  
41204 /**
41205  * @class Roo.bootstrap.layout.Basic
41206  * @extends Roo.util.Observable
41207  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
41208  * and does not have a titlebar, tabs or any other features. All it does is size and position 
41209  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
41210  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41211  * @cfg {string}   region  the region that it inhabits..
41212  * @cfg {bool}   skipConfig skip config?
41213  * 
41214
41215  */
41216 Roo.bootstrap.layout.Basic = function(config){
41217     
41218     this.mgr = config.mgr;
41219     
41220     this.position = config.region;
41221     
41222     var skipConfig = config.skipConfig;
41223     
41224     this.events = {
41225         /**
41226          * @scope Roo.BasicLayoutRegion
41227          */
41228         
41229         /**
41230          * @event beforeremove
41231          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
41232          * @param {Roo.LayoutRegion} this
41233          * @param {Roo.ContentPanel} panel The panel
41234          * @param {Object} e The cancel event object
41235          */
41236         "beforeremove" : true,
41237         /**
41238          * @event invalidated
41239          * Fires when the layout for this region is changed.
41240          * @param {Roo.LayoutRegion} this
41241          */
41242         "invalidated" : true,
41243         /**
41244          * @event visibilitychange
41245          * Fires when this region is shown or hidden 
41246          * @param {Roo.LayoutRegion} this
41247          * @param {Boolean} visibility true or false
41248          */
41249         "visibilitychange" : true,
41250         /**
41251          * @event paneladded
41252          * Fires when a panel is added. 
41253          * @param {Roo.LayoutRegion} this
41254          * @param {Roo.ContentPanel} panel The panel
41255          */
41256         "paneladded" : true,
41257         /**
41258          * @event panelremoved
41259          * Fires when a panel is removed. 
41260          * @param {Roo.LayoutRegion} this
41261          * @param {Roo.ContentPanel} panel The panel
41262          */
41263         "panelremoved" : true,
41264         /**
41265          * @event beforecollapse
41266          * Fires when this region before collapse.
41267          * @param {Roo.LayoutRegion} this
41268          */
41269         "beforecollapse" : true,
41270         /**
41271          * @event collapsed
41272          * Fires when this region is collapsed.
41273          * @param {Roo.LayoutRegion} this
41274          */
41275         "collapsed" : true,
41276         /**
41277          * @event expanded
41278          * Fires when this region is expanded.
41279          * @param {Roo.LayoutRegion} this
41280          */
41281         "expanded" : true,
41282         /**
41283          * @event slideshow
41284          * Fires when this region is slid into view.
41285          * @param {Roo.LayoutRegion} this
41286          */
41287         "slideshow" : true,
41288         /**
41289          * @event slidehide
41290          * Fires when this region slides out of view. 
41291          * @param {Roo.LayoutRegion} this
41292          */
41293         "slidehide" : true,
41294         /**
41295          * @event panelactivated
41296          * Fires when a panel is activated. 
41297          * @param {Roo.LayoutRegion} this
41298          * @param {Roo.ContentPanel} panel The activated panel
41299          */
41300         "panelactivated" : true,
41301         /**
41302          * @event resized
41303          * Fires when the user resizes this region. 
41304          * @param {Roo.LayoutRegion} this
41305          * @param {Number} newSize The new size (width for east/west, height for north/south)
41306          */
41307         "resized" : true
41308     };
41309     /** A collection of panels in this region. @type Roo.util.MixedCollection */
41310     this.panels = new Roo.util.MixedCollection();
41311     this.panels.getKey = this.getPanelId.createDelegate(this);
41312     this.box = null;
41313     this.activePanel = null;
41314     // ensure listeners are added...
41315     
41316     if (config.listeners || config.events) {
41317         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
41318             listeners : config.listeners || {},
41319             events : config.events || {}
41320         });
41321     }
41322     
41323     if(skipConfig !== true){
41324         this.applyConfig(config);
41325     }
41326 };
41327
41328 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
41329 {
41330     getPanelId : function(p){
41331         return p.getId();
41332     },
41333     
41334     applyConfig : function(config){
41335         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41336         this.config = config;
41337         
41338     },
41339     
41340     /**
41341      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
41342      * the width, for horizontal (north, south) the height.
41343      * @param {Number} newSize The new width or height
41344      */
41345     resizeTo : function(newSize){
41346         var el = this.el ? this.el :
41347                  (this.activePanel ? this.activePanel.getEl() : null);
41348         if(el){
41349             switch(this.position){
41350                 case "east":
41351                 case "west":
41352                     el.setWidth(newSize);
41353                     this.fireEvent("resized", this, newSize);
41354                 break;
41355                 case "north":
41356                 case "south":
41357                     el.setHeight(newSize);
41358                     this.fireEvent("resized", this, newSize);
41359                 break;                
41360             }
41361         }
41362     },
41363     
41364     getBox : function(){
41365         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
41366     },
41367     
41368     getMargins : function(){
41369         return this.margins;
41370     },
41371     
41372     updateBox : function(box){
41373         this.box = box;
41374         var el = this.activePanel.getEl();
41375         el.dom.style.left = box.x + "px";
41376         el.dom.style.top = box.y + "px";
41377         this.activePanel.setSize(box.width, box.height);
41378     },
41379     
41380     /**
41381      * Returns the container element for this region.
41382      * @return {Roo.Element}
41383      */
41384     getEl : function(){
41385         return this.activePanel;
41386     },
41387     
41388     /**
41389      * Returns true if this region is currently visible.
41390      * @return {Boolean}
41391      */
41392     isVisible : function(){
41393         return this.activePanel ? true : false;
41394     },
41395     
41396     setActivePanel : function(panel){
41397         panel = this.getPanel(panel);
41398         if(this.activePanel && this.activePanel != panel){
41399             this.activePanel.setActiveState(false);
41400             this.activePanel.getEl().setLeftTop(-10000,-10000);
41401         }
41402         this.activePanel = panel;
41403         panel.setActiveState(true);
41404         if(this.box){
41405             panel.setSize(this.box.width, this.box.height);
41406         }
41407         this.fireEvent("panelactivated", this, panel);
41408         this.fireEvent("invalidated");
41409     },
41410     
41411     /**
41412      * Show the specified panel.
41413      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
41414      * @return {Roo.ContentPanel} The shown panel or null
41415      */
41416     showPanel : function(panel){
41417         panel = this.getPanel(panel);
41418         if(panel){
41419             this.setActivePanel(panel);
41420         }
41421         return panel;
41422     },
41423     
41424     /**
41425      * Get the active panel for this region.
41426      * @return {Roo.ContentPanel} The active panel or null
41427      */
41428     getActivePanel : function(){
41429         return this.activePanel;
41430     },
41431     
41432     /**
41433      * Add the passed ContentPanel(s)
41434      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
41435      * @return {Roo.ContentPanel} The panel added (if only one was added)
41436      */
41437     add : function(panel){
41438         if(arguments.length > 1){
41439             for(var i = 0, len = arguments.length; i < len; i++) {
41440                 this.add(arguments[i]);
41441             }
41442             return null;
41443         }
41444         if(this.hasPanel(panel)){
41445             this.showPanel(panel);
41446             return panel;
41447         }
41448         var el = panel.getEl();
41449         if(el.dom.parentNode != this.mgr.el.dom){
41450             this.mgr.el.dom.appendChild(el.dom);
41451         }
41452         if(panel.setRegion){
41453             panel.setRegion(this);
41454         }
41455         this.panels.add(panel);
41456         el.setStyle("position", "absolute");
41457         if(!panel.background){
41458             this.setActivePanel(panel);
41459             if(this.config.initialSize && this.panels.getCount()==1){
41460                 this.resizeTo(this.config.initialSize);
41461             }
41462         }
41463         this.fireEvent("paneladded", this, panel);
41464         return panel;
41465     },
41466     
41467     /**
41468      * Returns true if the panel is in this region.
41469      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41470      * @return {Boolean}
41471      */
41472     hasPanel : function(panel){
41473         if(typeof panel == "object"){ // must be panel obj
41474             panel = panel.getId();
41475         }
41476         return this.getPanel(panel) ? true : false;
41477     },
41478     
41479     /**
41480      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
41481      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41482      * @param {Boolean} preservePanel Overrides the config preservePanel option
41483      * @return {Roo.ContentPanel} The panel that was removed
41484      */
41485     remove : function(panel, preservePanel){
41486         panel = this.getPanel(panel);
41487         if(!panel){
41488             return null;
41489         }
41490         var e = {};
41491         this.fireEvent("beforeremove", this, panel, e);
41492         if(e.cancel === true){
41493             return null;
41494         }
41495         var panelId = panel.getId();
41496         this.panels.removeKey(panelId);
41497         return panel;
41498     },
41499     
41500     /**
41501      * Returns the panel specified or null if it's not in this region.
41502      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41503      * @return {Roo.ContentPanel}
41504      */
41505     getPanel : function(id){
41506         if(typeof id == "object"){ // must be panel obj
41507             return id;
41508         }
41509         return this.panels.get(id);
41510     },
41511     
41512     /**
41513      * Returns this regions position (north/south/east/west/center).
41514      * @return {String} 
41515      */
41516     getPosition: function(){
41517         return this.position;    
41518     }
41519 });/*
41520  * Based on:
41521  * Ext JS Library 1.1.1
41522  * Copyright(c) 2006-2007, Ext JS, LLC.
41523  *
41524  * Originally Released Under LGPL - original licence link has changed is not relivant.
41525  *
41526  * Fork - LGPL
41527  * <script type="text/javascript">
41528  */
41529  
41530 /**
41531  * @class Roo.bootstrap.layout.Region
41532  * @extends Roo.bootstrap.layout.Basic
41533  * This class represents a region in a layout manager.
41534  
41535  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
41536  * @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})
41537  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
41538  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
41539  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
41540  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
41541  * @cfg {String}    title           The title for the region (overrides panel titles)
41542  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
41543  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
41544  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
41545  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
41546  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
41547  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
41548  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
41549  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
41550  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
41551  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
41552
41553  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
41554  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
41555  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
41556  * @cfg {Number}    width           For East/West panels
41557  * @cfg {Number}    height          For North/South panels
41558  * @cfg {Boolean}   split           To show the splitter
41559  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
41560  * 
41561  * @cfg {string}   cls             Extra CSS classes to add to region
41562  * 
41563  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41564  * @cfg {string}   region  the region that it inhabits..
41565  *
41566
41567  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
41568  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
41569
41570  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
41571  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
41572  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
41573  */
41574 Roo.bootstrap.layout.Region = function(config)
41575 {
41576     this.applyConfig(config);
41577
41578     var mgr = config.mgr;
41579     var pos = config.region;
41580     config.skipConfig = true;
41581     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
41582     
41583     if (mgr.el) {
41584         this.onRender(mgr.el);   
41585     }
41586      
41587     this.visible = true;
41588     this.collapsed = false;
41589     this.unrendered_panels = [];
41590 };
41591
41592 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
41593
41594     position: '', // set by wrapper (eg. north/south etc..)
41595     unrendered_panels : null,  // unrendered panels.
41596     
41597     tabPosition : false,
41598     
41599     mgr: false, // points to 'Border'
41600     
41601     
41602     createBody : function(){
41603         /** This region's body element 
41604         * @type Roo.Element */
41605         this.bodyEl = this.el.createChild({
41606                 tag: "div",
41607                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
41608         });
41609     },
41610
41611     onRender: function(ctr, pos)
41612     {
41613         var dh = Roo.DomHelper;
41614         /** This region's container element 
41615         * @type Roo.Element */
41616         this.el = dh.append(ctr.dom, {
41617                 tag: "div",
41618                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
41619             }, true);
41620         /** This region's title element 
41621         * @type Roo.Element */
41622     
41623         this.titleEl = dh.append(this.el.dom,  {
41624                 tag: "div",
41625                 unselectable: "on",
41626                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
41627                 children:[
41628                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
41629                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
41630                 ]
41631             }, true);
41632         
41633         this.titleEl.enableDisplayMode();
41634         /** This region's title text element 
41635         * @type HTMLElement */
41636         this.titleTextEl = this.titleEl.dom.firstChild;
41637         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
41638         /*
41639         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
41640         this.closeBtn.enableDisplayMode();
41641         this.closeBtn.on("click", this.closeClicked, this);
41642         this.closeBtn.hide();
41643     */
41644         this.createBody(this.config);
41645         if(this.config.hideWhenEmpty){
41646             this.hide();
41647             this.on("paneladded", this.validateVisibility, this);
41648             this.on("panelremoved", this.validateVisibility, this);
41649         }
41650         if(this.autoScroll){
41651             this.bodyEl.setStyle("overflow", "auto");
41652         }else{
41653             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
41654         }
41655         //if(c.titlebar !== false){
41656             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
41657                 this.titleEl.hide();
41658             }else{
41659                 this.titleEl.show();
41660                 if(this.config.title){
41661                     this.titleTextEl.innerHTML = this.config.title;
41662                 }
41663             }
41664         //}
41665         if(this.config.collapsed){
41666             this.collapse(true);
41667         }
41668         if(this.config.hidden){
41669             this.hide();
41670         }
41671         
41672         if (this.unrendered_panels && this.unrendered_panels.length) {
41673             for (var i =0;i< this.unrendered_panels.length; i++) {
41674                 this.add(this.unrendered_panels[i]);
41675             }
41676             this.unrendered_panels = null;
41677             
41678         }
41679         
41680     },
41681     
41682     applyConfig : function(c)
41683     {
41684         /*
41685          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
41686             var dh = Roo.DomHelper;
41687             if(c.titlebar !== false){
41688                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
41689                 this.collapseBtn.on("click", this.collapse, this);
41690                 this.collapseBtn.enableDisplayMode();
41691                 /*
41692                 if(c.showPin === true || this.showPin){
41693                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
41694                     this.stickBtn.enableDisplayMode();
41695                     this.stickBtn.on("click", this.expand, this);
41696                     this.stickBtn.hide();
41697                 }
41698                 
41699             }
41700             */
41701             /** This region's collapsed element
41702             * @type Roo.Element */
41703             /*
41704              *
41705             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
41706                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
41707             ]}, true);
41708             
41709             if(c.floatable !== false){
41710                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
41711                this.collapsedEl.on("click", this.collapseClick, this);
41712             }
41713
41714             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
41715                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
41716                    id: "message", unselectable: "on", style:{"float":"left"}});
41717                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
41718              }
41719             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
41720             this.expandBtn.on("click", this.expand, this);
41721             
41722         }
41723         
41724         if(this.collapseBtn){
41725             this.collapseBtn.setVisible(c.collapsible == true);
41726         }
41727         
41728         this.cmargins = c.cmargins || this.cmargins ||
41729                          (this.position == "west" || this.position == "east" ?
41730                              {top: 0, left: 2, right:2, bottom: 0} :
41731                              {top: 2, left: 0, right:0, bottom: 2});
41732         */
41733         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41734         
41735         
41736         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
41737         
41738         this.autoScroll = c.autoScroll || false;
41739         
41740         
41741        
41742         
41743         this.duration = c.duration || .30;
41744         this.slideDuration = c.slideDuration || .45;
41745         this.config = c;
41746        
41747     },
41748     /**
41749      * Returns true if this region is currently visible.
41750      * @return {Boolean}
41751      */
41752     isVisible : function(){
41753         return this.visible;
41754     },
41755
41756     /**
41757      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
41758      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
41759      */
41760     //setCollapsedTitle : function(title){
41761     //    title = title || "&#160;";
41762      //   if(this.collapsedTitleTextEl){
41763       //      this.collapsedTitleTextEl.innerHTML = title;
41764        // }
41765     //},
41766
41767     getBox : function(){
41768         var b;
41769       //  if(!this.collapsed){
41770             b = this.el.getBox(false, true);
41771        // }else{
41772           //  b = this.collapsedEl.getBox(false, true);
41773         //}
41774         return b;
41775     },
41776
41777     getMargins : function(){
41778         return this.margins;
41779         //return this.collapsed ? this.cmargins : this.margins;
41780     },
41781 /*
41782     highlight : function(){
41783         this.el.addClass("x-layout-panel-dragover");
41784     },
41785
41786     unhighlight : function(){
41787         this.el.removeClass("x-layout-panel-dragover");
41788     },
41789 */
41790     updateBox : function(box)
41791     {
41792         if (!this.bodyEl) {
41793             return; // not rendered yet..
41794         }
41795         
41796         this.box = box;
41797         if(!this.collapsed){
41798             this.el.dom.style.left = box.x + "px";
41799             this.el.dom.style.top = box.y + "px";
41800             this.updateBody(box.width, box.height);
41801         }else{
41802             this.collapsedEl.dom.style.left = box.x + "px";
41803             this.collapsedEl.dom.style.top = box.y + "px";
41804             this.collapsedEl.setSize(box.width, box.height);
41805         }
41806         if(this.tabs){
41807             this.tabs.autoSizeTabs();
41808         }
41809     },
41810
41811     updateBody : function(w, h)
41812     {
41813         if(w !== null){
41814             this.el.setWidth(w);
41815             w -= this.el.getBorderWidth("rl");
41816             if(this.config.adjustments){
41817                 w += this.config.adjustments[0];
41818             }
41819         }
41820         if(h !== null && h > 0){
41821             this.el.setHeight(h);
41822             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
41823             h -= this.el.getBorderWidth("tb");
41824             if(this.config.adjustments){
41825                 h += this.config.adjustments[1];
41826             }
41827             this.bodyEl.setHeight(h);
41828             if(this.tabs){
41829                 h = this.tabs.syncHeight(h);
41830             }
41831         }
41832         if(this.panelSize){
41833             w = w !== null ? w : this.panelSize.width;
41834             h = h !== null ? h : this.panelSize.height;
41835         }
41836         if(this.activePanel){
41837             var el = this.activePanel.getEl();
41838             w = w !== null ? w : el.getWidth();
41839             h = h !== null ? h : el.getHeight();
41840             this.panelSize = {width: w, height: h};
41841             this.activePanel.setSize(w, h);
41842         }
41843         if(Roo.isIE && this.tabs){
41844             this.tabs.el.repaint();
41845         }
41846     },
41847
41848     /**
41849      * Returns the container element for this region.
41850      * @return {Roo.Element}
41851      */
41852     getEl : function(){
41853         return this.el;
41854     },
41855
41856     /**
41857      * Hides this region.
41858      */
41859     hide : function(){
41860         //if(!this.collapsed){
41861             this.el.dom.style.left = "-2000px";
41862             this.el.hide();
41863         //}else{
41864          //   this.collapsedEl.dom.style.left = "-2000px";
41865          //   this.collapsedEl.hide();
41866        // }
41867         this.visible = false;
41868         this.fireEvent("visibilitychange", this, false);
41869     },
41870
41871     /**
41872      * Shows this region if it was previously hidden.
41873      */
41874     show : function(){
41875         //if(!this.collapsed){
41876             this.el.show();
41877         //}else{
41878         //    this.collapsedEl.show();
41879        // }
41880         this.visible = true;
41881         this.fireEvent("visibilitychange", this, true);
41882     },
41883 /*
41884     closeClicked : function(){
41885         if(this.activePanel){
41886             this.remove(this.activePanel);
41887         }
41888     },
41889
41890     collapseClick : function(e){
41891         if(this.isSlid){
41892            e.stopPropagation();
41893            this.slideIn();
41894         }else{
41895            e.stopPropagation();
41896            this.slideOut();
41897         }
41898     },
41899 */
41900     /**
41901      * Collapses this region.
41902      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
41903      */
41904     /*
41905     collapse : function(skipAnim, skipCheck = false){
41906         if(this.collapsed) {
41907             return;
41908         }
41909         
41910         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
41911             
41912             this.collapsed = true;
41913             if(this.split){
41914                 this.split.el.hide();
41915             }
41916             if(this.config.animate && skipAnim !== true){
41917                 this.fireEvent("invalidated", this);
41918                 this.animateCollapse();
41919             }else{
41920                 this.el.setLocation(-20000,-20000);
41921                 this.el.hide();
41922                 this.collapsedEl.show();
41923                 this.fireEvent("collapsed", this);
41924                 this.fireEvent("invalidated", this);
41925             }
41926         }
41927         
41928     },
41929 */
41930     animateCollapse : function(){
41931         // overridden
41932     },
41933
41934     /**
41935      * Expands this region if it was previously collapsed.
41936      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
41937      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
41938      */
41939     /*
41940     expand : function(e, skipAnim){
41941         if(e) {
41942             e.stopPropagation();
41943         }
41944         if(!this.collapsed || this.el.hasActiveFx()) {
41945             return;
41946         }
41947         if(this.isSlid){
41948             this.afterSlideIn();
41949             skipAnim = true;
41950         }
41951         this.collapsed = false;
41952         if(this.config.animate && skipAnim !== true){
41953             this.animateExpand();
41954         }else{
41955             this.el.show();
41956             if(this.split){
41957                 this.split.el.show();
41958             }
41959             this.collapsedEl.setLocation(-2000,-2000);
41960             this.collapsedEl.hide();
41961             this.fireEvent("invalidated", this);
41962             this.fireEvent("expanded", this);
41963         }
41964     },
41965 */
41966     animateExpand : function(){
41967         // overridden
41968     },
41969
41970     initTabs : function()
41971     {
41972         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
41973         
41974         var ts = new Roo.bootstrap.panel.Tabs({
41975             el: this.bodyEl.dom,
41976             region : this,
41977             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
41978             disableTooltips: this.config.disableTabTips,
41979             toolbar : this.config.toolbar
41980         });
41981         
41982         if(this.config.hideTabs){
41983             ts.stripWrap.setDisplayed(false);
41984         }
41985         this.tabs = ts;
41986         ts.resizeTabs = this.config.resizeTabs === true;
41987         ts.minTabWidth = this.config.minTabWidth || 40;
41988         ts.maxTabWidth = this.config.maxTabWidth || 250;
41989         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
41990         ts.monitorResize = false;
41991         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
41992         ts.bodyEl.addClass('roo-layout-tabs-body');
41993         this.panels.each(this.initPanelAsTab, this);
41994     },
41995
41996     initPanelAsTab : function(panel){
41997         var ti = this.tabs.addTab(
41998             panel.getEl().id,
41999             panel.getTitle(),
42000             null,
42001             this.config.closeOnTab && panel.isClosable(),
42002             panel.tpl
42003         );
42004         if(panel.tabTip !== undefined){
42005             ti.setTooltip(panel.tabTip);
42006         }
42007         ti.on("activate", function(){
42008               this.setActivePanel(panel);
42009         }, this);
42010         
42011         if(this.config.closeOnTab){
42012             ti.on("beforeclose", function(t, e){
42013                 e.cancel = true;
42014                 this.remove(panel);
42015             }, this);
42016         }
42017         
42018         panel.tabItem = ti;
42019         
42020         return ti;
42021     },
42022
42023     updatePanelTitle : function(panel, title)
42024     {
42025         if(this.activePanel == panel){
42026             this.updateTitle(title);
42027         }
42028         if(this.tabs){
42029             var ti = this.tabs.getTab(panel.getEl().id);
42030             ti.setText(title);
42031             if(panel.tabTip !== undefined){
42032                 ti.setTooltip(panel.tabTip);
42033             }
42034         }
42035     },
42036
42037     updateTitle : function(title){
42038         if(this.titleTextEl && !this.config.title){
42039             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
42040         }
42041     },
42042
42043     setActivePanel : function(panel)
42044     {
42045         panel = this.getPanel(panel);
42046         if(this.activePanel && this.activePanel != panel){
42047             if(this.activePanel.setActiveState(false) === false){
42048                 return;
42049             }
42050         }
42051         this.activePanel = panel;
42052         panel.setActiveState(true);
42053         if(this.panelSize){
42054             panel.setSize(this.panelSize.width, this.panelSize.height);
42055         }
42056         if(this.closeBtn){
42057             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
42058         }
42059         this.updateTitle(panel.getTitle());
42060         if(this.tabs){
42061             this.fireEvent("invalidated", this);
42062         }
42063         this.fireEvent("panelactivated", this, panel);
42064     },
42065
42066     /**
42067      * Shows the specified panel.
42068      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
42069      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
42070      */
42071     showPanel : function(panel)
42072     {
42073         panel = this.getPanel(panel);
42074         if(panel){
42075             if(this.tabs){
42076                 var tab = this.tabs.getTab(panel.getEl().id);
42077                 if(tab.isHidden()){
42078                     this.tabs.unhideTab(tab.id);
42079                 }
42080                 tab.activate();
42081             }else{
42082                 this.setActivePanel(panel);
42083             }
42084         }
42085         return panel;
42086     },
42087
42088     /**
42089      * Get the active panel for this region.
42090      * @return {Roo.ContentPanel} The active panel or null
42091      */
42092     getActivePanel : function(){
42093         return this.activePanel;
42094     },
42095
42096     validateVisibility : function(){
42097         if(this.panels.getCount() < 1){
42098             this.updateTitle("&#160;");
42099             this.closeBtn.hide();
42100             this.hide();
42101         }else{
42102             if(!this.isVisible()){
42103                 this.show();
42104             }
42105         }
42106     },
42107
42108     /**
42109      * Adds the passed ContentPanel(s) to this region.
42110      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42111      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
42112      */
42113     add : function(panel)
42114     {
42115         if(arguments.length > 1){
42116             for(var i = 0, len = arguments.length; i < len; i++) {
42117                 this.add(arguments[i]);
42118             }
42119             return null;
42120         }
42121         
42122         // if we have not been rendered yet, then we can not really do much of this..
42123         if (!this.bodyEl) {
42124             this.unrendered_panels.push(panel);
42125             return panel;
42126         }
42127         
42128         
42129         
42130         
42131         if(this.hasPanel(panel)){
42132             this.showPanel(panel);
42133             return panel;
42134         }
42135         panel.setRegion(this);
42136         this.panels.add(panel);
42137        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
42138             // sinle panel - no tab...?? would it not be better to render it with the tabs,
42139             // and hide them... ???
42140             this.bodyEl.dom.appendChild(panel.getEl().dom);
42141             if(panel.background !== true){
42142                 this.setActivePanel(panel);
42143             }
42144             this.fireEvent("paneladded", this, panel);
42145             return panel;
42146         }
42147         */
42148         if(!this.tabs){
42149             this.initTabs();
42150         }else{
42151             this.initPanelAsTab(panel);
42152         }
42153         
42154         
42155         if(panel.background !== true){
42156             this.tabs.activate(panel.getEl().id);
42157         }
42158         this.fireEvent("paneladded", this, panel);
42159         return panel;
42160     },
42161
42162     /**
42163      * Hides the tab for the specified panel.
42164      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42165      */
42166     hidePanel : function(panel){
42167         if(this.tabs && (panel = this.getPanel(panel))){
42168             this.tabs.hideTab(panel.getEl().id);
42169         }
42170     },
42171
42172     /**
42173      * Unhides the tab for a previously hidden panel.
42174      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42175      */
42176     unhidePanel : function(panel){
42177         if(this.tabs && (panel = this.getPanel(panel))){
42178             this.tabs.unhideTab(panel.getEl().id);
42179         }
42180     },
42181
42182     clearPanels : function(){
42183         while(this.panels.getCount() > 0){
42184              this.remove(this.panels.first());
42185         }
42186     },
42187
42188     /**
42189      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42190      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42191      * @param {Boolean} preservePanel Overrides the config preservePanel option
42192      * @return {Roo.ContentPanel} The panel that was removed
42193      */
42194     remove : function(panel, preservePanel)
42195     {
42196         panel = this.getPanel(panel);
42197         if(!panel){
42198             return null;
42199         }
42200         var e = {};
42201         this.fireEvent("beforeremove", this, panel, e);
42202         if(e.cancel === true){
42203             return null;
42204         }
42205         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
42206         var panelId = panel.getId();
42207         this.panels.removeKey(panelId);
42208         if(preservePanel){
42209             document.body.appendChild(panel.getEl().dom);
42210         }
42211         if(this.tabs){
42212             this.tabs.removeTab(panel.getEl().id);
42213         }else if (!preservePanel){
42214             this.bodyEl.dom.removeChild(panel.getEl().dom);
42215         }
42216         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
42217             var p = this.panels.first();
42218             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
42219             tempEl.appendChild(p.getEl().dom);
42220             this.bodyEl.update("");
42221             this.bodyEl.dom.appendChild(p.getEl().dom);
42222             tempEl = null;
42223             this.updateTitle(p.getTitle());
42224             this.tabs = null;
42225             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
42226             this.setActivePanel(p);
42227         }
42228         panel.setRegion(null);
42229         if(this.activePanel == panel){
42230             this.activePanel = null;
42231         }
42232         if(this.config.autoDestroy !== false && preservePanel !== true){
42233             try{panel.destroy();}catch(e){}
42234         }
42235         this.fireEvent("panelremoved", this, panel);
42236         return panel;
42237     },
42238
42239     /**
42240      * Returns the TabPanel component used by this region
42241      * @return {Roo.TabPanel}
42242      */
42243     getTabs : function(){
42244         return this.tabs;
42245     },
42246
42247     createTool : function(parentEl, className){
42248         var btn = Roo.DomHelper.append(parentEl, {
42249             tag: "div",
42250             cls: "x-layout-tools-button",
42251             children: [ {
42252                 tag: "div",
42253                 cls: "roo-layout-tools-button-inner " + className,
42254                 html: "&#160;"
42255             }]
42256         }, true);
42257         btn.addClassOnOver("roo-layout-tools-button-over");
42258         return btn;
42259     }
42260 });/*
42261  * Based on:
42262  * Ext JS Library 1.1.1
42263  * Copyright(c) 2006-2007, Ext JS, LLC.
42264  *
42265  * Originally Released Under LGPL - original licence link has changed is not relivant.
42266  *
42267  * Fork - LGPL
42268  * <script type="text/javascript">
42269  */
42270  
42271
42272
42273 /**
42274  * @class Roo.SplitLayoutRegion
42275  * @extends Roo.LayoutRegion
42276  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
42277  */
42278 Roo.bootstrap.layout.Split = function(config){
42279     this.cursor = config.cursor;
42280     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
42281 };
42282
42283 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
42284 {
42285     splitTip : "Drag to resize.",
42286     collapsibleSplitTip : "Drag to resize. Double click to hide.",
42287     useSplitTips : false,
42288
42289     applyConfig : function(config){
42290         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
42291     },
42292     
42293     onRender : function(ctr,pos) {
42294         
42295         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
42296         if(!this.config.split){
42297             return;
42298         }
42299         if(!this.split){
42300             
42301             var splitEl = Roo.DomHelper.append(ctr.dom,  {
42302                             tag: "div",
42303                             id: this.el.id + "-split",
42304                             cls: "roo-layout-split roo-layout-split-"+this.position,
42305                             html: "&#160;"
42306             });
42307             /** The SplitBar for this region 
42308             * @type Roo.SplitBar */
42309             // does not exist yet...
42310             Roo.log([this.position, this.orientation]);
42311             
42312             this.split = new Roo.bootstrap.SplitBar({
42313                 dragElement : splitEl,
42314                 resizingElement: this.el,
42315                 orientation : this.orientation
42316             });
42317             
42318             this.split.on("moved", this.onSplitMove, this);
42319             this.split.useShim = this.config.useShim === true;
42320             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
42321             if(this.useSplitTips){
42322                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
42323             }
42324             //if(config.collapsible){
42325             //    this.split.el.on("dblclick", this.collapse,  this);
42326             //}
42327         }
42328         if(typeof this.config.minSize != "undefined"){
42329             this.split.minSize = this.config.minSize;
42330         }
42331         if(typeof this.config.maxSize != "undefined"){
42332             this.split.maxSize = this.config.maxSize;
42333         }
42334         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
42335             this.hideSplitter();
42336         }
42337         
42338     },
42339
42340     getHMaxSize : function(){
42341          var cmax = this.config.maxSize || 10000;
42342          var center = this.mgr.getRegion("center");
42343          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
42344     },
42345
42346     getVMaxSize : function(){
42347          var cmax = this.config.maxSize || 10000;
42348          var center = this.mgr.getRegion("center");
42349          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
42350     },
42351
42352     onSplitMove : function(split, newSize){
42353         this.fireEvent("resized", this, newSize);
42354     },
42355     
42356     /** 
42357      * Returns the {@link Roo.SplitBar} for this region.
42358      * @return {Roo.SplitBar}
42359      */
42360     getSplitBar : function(){
42361         return this.split;
42362     },
42363     
42364     hide : function(){
42365         this.hideSplitter();
42366         Roo.bootstrap.layout.Split.superclass.hide.call(this);
42367     },
42368
42369     hideSplitter : function(){
42370         if(this.split){
42371             this.split.el.setLocation(-2000,-2000);
42372             this.split.el.hide();
42373         }
42374     },
42375
42376     show : function(){
42377         if(this.split){
42378             this.split.el.show();
42379         }
42380         Roo.bootstrap.layout.Split.superclass.show.call(this);
42381     },
42382     
42383     beforeSlide: function(){
42384         if(Roo.isGecko){// firefox overflow auto bug workaround
42385             this.bodyEl.clip();
42386             if(this.tabs) {
42387                 this.tabs.bodyEl.clip();
42388             }
42389             if(this.activePanel){
42390                 this.activePanel.getEl().clip();
42391                 
42392                 if(this.activePanel.beforeSlide){
42393                     this.activePanel.beforeSlide();
42394                 }
42395             }
42396         }
42397     },
42398     
42399     afterSlide : function(){
42400         if(Roo.isGecko){// firefox overflow auto bug workaround
42401             this.bodyEl.unclip();
42402             if(this.tabs) {
42403                 this.tabs.bodyEl.unclip();
42404             }
42405             if(this.activePanel){
42406                 this.activePanel.getEl().unclip();
42407                 if(this.activePanel.afterSlide){
42408                     this.activePanel.afterSlide();
42409                 }
42410             }
42411         }
42412     },
42413
42414     initAutoHide : function(){
42415         if(this.autoHide !== false){
42416             if(!this.autoHideHd){
42417                 var st = new Roo.util.DelayedTask(this.slideIn, this);
42418                 this.autoHideHd = {
42419                     "mouseout": function(e){
42420                         if(!e.within(this.el, true)){
42421                             st.delay(500);
42422                         }
42423                     },
42424                     "mouseover" : function(e){
42425                         st.cancel();
42426                     },
42427                     scope : this
42428                 };
42429             }
42430             this.el.on(this.autoHideHd);
42431         }
42432     },
42433
42434     clearAutoHide : function(){
42435         if(this.autoHide !== false){
42436             this.el.un("mouseout", this.autoHideHd.mouseout);
42437             this.el.un("mouseover", this.autoHideHd.mouseover);
42438         }
42439     },
42440
42441     clearMonitor : function(){
42442         Roo.get(document).un("click", this.slideInIf, this);
42443     },
42444
42445     // these names are backwards but not changed for compat
42446     slideOut : function(){
42447         if(this.isSlid || this.el.hasActiveFx()){
42448             return;
42449         }
42450         this.isSlid = true;
42451         if(this.collapseBtn){
42452             this.collapseBtn.hide();
42453         }
42454         this.closeBtnState = this.closeBtn.getStyle('display');
42455         this.closeBtn.hide();
42456         if(this.stickBtn){
42457             this.stickBtn.show();
42458         }
42459         this.el.show();
42460         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
42461         this.beforeSlide();
42462         this.el.setStyle("z-index", 10001);
42463         this.el.slideIn(this.getSlideAnchor(), {
42464             callback: function(){
42465                 this.afterSlide();
42466                 this.initAutoHide();
42467                 Roo.get(document).on("click", this.slideInIf, this);
42468                 this.fireEvent("slideshow", this);
42469             },
42470             scope: this,
42471             block: true
42472         });
42473     },
42474
42475     afterSlideIn : function(){
42476         this.clearAutoHide();
42477         this.isSlid = false;
42478         this.clearMonitor();
42479         this.el.setStyle("z-index", "");
42480         if(this.collapseBtn){
42481             this.collapseBtn.show();
42482         }
42483         this.closeBtn.setStyle('display', this.closeBtnState);
42484         if(this.stickBtn){
42485             this.stickBtn.hide();
42486         }
42487         this.fireEvent("slidehide", this);
42488     },
42489
42490     slideIn : function(cb){
42491         if(!this.isSlid || this.el.hasActiveFx()){
42492             Roo.callback(cb);
42493             return;
42494         }
42495         this.isSlid = false;
42496         this.beforeSlide();
42497         this.el.slideOut(this.getSlideAnchor(), {
42498             callback: function(){
42499                 this.el.setLeftTop(-10000, -10000);
42500                 this.afterSlide();
42501                 this.afterSlideIn();
42502                 Roo.callback(cb);
42503             },
42504             scope: this,
42505             block: true
42506         });
42507     },
42508     
42509     slideInIf : function(e){
42510         if(!e.within(this.el)){
42511             this.slideIn();
42512         }
42513     },
42514
42515     animateCollapse : function(){
42516         this.beforeSlide();
42517         this.el.setStyle("z-index", 20000);
42518         var anchor = this.getSlideAnchor();
42519         this.el.slideOut(anchor, {
42520             callback : function(){
42521                 this.el.setStyle("z-index", "");
42522                 this.collapsedEl.slideIn(anchor, {duration:.3});
42523                 this.afterSlide();
42524                 this.el.setLocation(-10000,-10000);
42525                 this.el.hide();
42526                 this.fireEvent("collapsed", this);
42527             },
42528             scope: this,
42529             block: true
42530         });
42531     },
42532
42533     animateExpand : function(){
42534         this.beforeSlide();
42535         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
42536         this.el.setStyle("z-index", 20000);
42537         this.collapsedEl.hide({
42538             duration:.1
42539         });
42540         this.el.slideIn(this.getSlideAnchor(), {
42541             callback : function(){
42542                 this.el.setStyle("z-index", "");
42543                 this.afterSlide();
42544                 if(this.split){
42545                     this.split.el.show();
42546                 }
42547                 this.fireEvent("invalidated", this);
42548                 this.fireEvent("expanded", this);
42549             },
42550             scope: this,
42551             block: true
42552         });
42553     },
42554
42555     anchors : {
42556         "west" : "left",
42557         "east" : "right",
42558         "north" : "top",
42559         "south" : "bottom"
42560     },
42561
42562     sanchors : {
42563         "west" : "l",
42564         "east" : "r",
42565         "north" : "t",
42566         "south" : "b"
42567     },
42568
42569     canchors : {
42570         "west" : "tl-tr",
42571         "east" : "tr-tl",
42572         "north" : "tl-bl",
42573         "south" : "bl-tl"
42574     },
42575
42576     getAnchor : function(){
42577         return this.anchors[this.position];
42578     },
42579
42580     getCollapseAnchor : function(){
42581         return this.canchors[this.position];
42582     },
42583
42584     getSlideAnchor : function(){
42585         return this.sanchors[this.position];
42586     },
42587
42588     getAlignAdj : function(){
42589         var cm = this.cmargins;
42590         switch(this.position){
42591             case "west":
42592                 return [0, 0];
42593             break;
42594             case "east":
42595                 return [0, 0];
42596             break;
42597             case "north":
42598                 return [0, 0];
42599             break;
42600             case "south":
42601                 return [0, 0];
42602             break;
42603         }
42604     },
42605
42606     getExpandAdj : function(){
42607         var c = this.collapsedEl, cm = this.cmargins;
42608         switch(this.position){
42609             case "west":
42610                 return [-(cm.right+c.getWidth()+cm.left), 0];
42611             break;
42612             case "east":
42613                 return [cm.right+c.getWidth()+cm.left, 0];
42614             break;
42615             case "north":
42616                 return [0, -(cm.top+cm.bottom+c.getHeight())];
42617             break;
42618             case "south":
42619                 return [0, cm.top+cm.bottom+c.getHeight()];
42620             break;
42621         }
42622     }
42623 });/*
42624  * Based on:
42625  * Ext JS Library 1.1.1
42626  * Copyright(c) 2006-2007, Ext JS, LLC.
42627  *
42628  * Originally Released Under LGPL - original licence link has changed is not relivant.
42629  *
42630  * Fork - LGPL
42631  * <script type="text/javascript">
42632  */
42633 /*
42634  * These classes are private internal classes
42635  */
42636 Roo.bootstrap.layout.Center = function(config){
42637     config.region = "center";
42638     Roo.bootstrap.layout.Region.call(this, config);
42639     this.visible = true;
42640     this.minWidth = config.minWidth || 20;
42641     this.minHeight = config.minHeight || 20;
42642 };
42643
42644 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
42645     hide : function(){
42646         // center panel can't be hidden
42647     },
42648     
42649     show : function(){
42650         // center panel can't be hidden
42651     },
42652     
42653     getMinWidth: function(){
42654         return this.minWidth;
42655     },
42656     
42657     getMinHeight: function(){
42658         return this.minHeight;
42659     }
42660 });
42661
42662
42663
42664
42665  
42666
42667
42668
42669
42670
42671
42672 Roo.bootstrap.layout.North = function(config)
42673 {
42674     config.region = 'north';
42675     config.cursor = 'n-resize';
42676     
42677     Roo.bootstrap.layout.Split.call(this, config);
42678     
42679     
42680     if(this.split){
42681         this.split.placement = Roo.bootstrap.SplitBar.TOP;
42682         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42683         this.split.el.addClass("roo-layout-split-v");
42684     }
42685     //var size = config.initialSize || config.height;
42686     //if(this.el && typeof size != "undefined"){
42687     //    this.el.setHeight(size);
42688     //}
42689 };
42690 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
42691 {
42692     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42693      
42694      
42695     onRender : function(ctr, pos)
42696     {
42697         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42698         var size = this.config.initialSize || this.config.height;
42699         if(this.el && typeof size != "undefined"){
42700             this.el.setHeight(size);
42701         }
42702     
42703     },
42704     
42705     getBox : function(){
42706         if(this.collapsed){
42707             return this.collapsedEl.getBox();
42708         }
42709         var box = this.el.getBox();
42710         if(this.split){
42711             box.height += this.split.el.getHeight();
42712         }
42713         return box;
42714     },
42715     
42716     updateBox : function(box){
42717         if(this.split && !this.collapsed){
42718             box.height -= this.split.el.getHeight();
42719             this.split.el.setLeft(box.x);
42720             this.split.el.setTop(box.y+box.height);
42721             this.split.el.setWidth(box.width);
42722         }
42723         if(this.collapsed){
42724             this.updateBody(box.width, null);
42725         }
42726         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42727     }
42728 });
42729
42730
42731
42732
42733
42734 Roo.bootstrap.layout.South = function(config){
42735     config.region = 'south';
42736     config.cursor = 's-resize';
42737     Roo.bootstrap.layout.Split.call(this, config);
42738     if(this.split){
42739         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
42740         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42741         this.split.el.addClass("roo-layout-split-v");
42742     }
42743     
42744 };
42745
42746 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
42747     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42748     
42749     onRender : function(ctr, pos)
42750     {
42751         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42752         var size = this.config.initialSize || this.config.height;
42753         if(this.el && typeof size != "undefined"){
42754             this.el.setHeight(size);
42755         }
42756     
42757     },
42758     
42759     getBox : function(){
42760         if(this.collapsed){
42761             return this.collapsedEl.getBox();
42762         }
42763         var box = this.el.getBox();
42764         if(this.split){
42765             var sh = this.split.el.getHeight();
42766             box.height += sh;
42767             box.y -= sh;
42768         }
42769         return box;
42770     },
42771     
42772     updateBox : function(box){
42773         if(this.split && !this.collapsed){
42774             var sh = this.split.el.getHeight();
42775             box.height -= sh;
42776             box.y += sh;
42777             this.split.el.setLeft(box.x);
42778             this.split.el.setTop(box.y-sh);
42779             this.split.el.setWidth(box.width);
42780         }
42781         if(this.collapsed){
42782             this.updateBody(box.width, null);
42783         }
42784         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42785     }
42786 });
42787
42788 Roo.bootstrap.layout.East = function(config){
42789     config.region = "east";
42790     config.cursor = "e-resize";
42791     Roo.bootstrap.layout.Split.call(this, config);
42792     if(this.split){
42793         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
42794         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
42795         this.split.el.addClass("roo-layout-split-h");
42796     }
42797     
42798 };
42799 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
42800     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
42801     
42802     onRender : function(ctr, pos)
42803     {
42804         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42805         var size = this.config.initialSize || this.config.width;
42806         if(this.el && typeof size != "undefined"){
42807             this.el.setWidth(size);
42808         }
42809     
42810     },
42811     
42812     getBox : function(){
42813         if(this.collapsed){
42814             return this.collapsedEl.getBox();
42815         }
42816         var box = this.el.getBox();
42817         if(this.split){
42818             var sw = this.split.el.getWidth();
42819             box.width += sw;
42820             box.x -= sw;
42821         }
42822         return box;
42823     },
42824
42825     updateBox : function(box){
42826         if(this.split && !this.collapsed){
42827             var sw = this.split.el.getWidth();
42828             box.width -= sw;
42829             this.split.el.setLeft(box.x);
42830             this.split.el.setTop(box.y);
42831             this.split.el.setHeight(box.height);
42832             box.x += sw;
42833         }
42834         if(this.collapsed){
42835             this.updateBody(null, box.height);
42836         }
42837         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42838     }
42839 });
42840
42841 Roo.bootstrap.layout.West = function(config){
42842     config.region = "west";
42843     config.cursor = "w-resize";
42844     
42845     Roo.bootstrap.layout.Split.call(this, config);
42846     if(this.split){
42847         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
42848         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
42849         this.split.el.addClass("roo-layout-split-h");
42850     }
42851     
42852 };
42853 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
42854     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
42855     
42856     onRender: function(ctr, pos)
42857     {
42858         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
42859         var size = this.config.initialSize || this.config.width;
42860         if(typeof size != "undefined"){
42861             this.el.setWidth(size);
42862         }
42863     },
42864     
42865     getBox : function(){
42866         if(this.collapsed){
42867             return this.collapsedEl.getBox();
42868         }
42869         var box = this.el.getBox();
42870         if (box.width == 0) {
42871             box.width = this.config.width; // kludge?
42872         }
42873         if(this.split){
42874             box.width += this.split.el.getWidth();
42875         }
42876         return box;
42877     },
42878     
42879     updateBox : function(box){
42880         if(this.split && !this.collapsed){
42881             var sw = this.split.el.getWidth();
42882             box.width -= sw;
42883             this.split.el.setLeft(box.x+box.width);
42884             this.split.el.setTop(box.y);
42885             this.split.el.setHeight(box.height);
42886         }
42887         if(this.collapsed){
42888             this.updateBody(null, box.height);
42889         }
42890         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42891     }
42892 });/*
42893  * Based on:
42894  * Ext JS Library 1.1.1
42895  * Copyright(c) 2006-2007, Ext JS, LLC.
42896  *
42897  * Originally Released Under LGPL - original licence link has changed is not relivant.
42898  *
42899  * Fork - LGPL
42900  * <script type="text/javascript">
42901  */
42902 /**
42903  * @class Roo.bootstrap.paenl.Content
42904  * @extends Roo.util.Observable
42905  * @children Roo.bootstrap.Component
42906  * @parent builder Roo.bootstrap.layout.Border
42907  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
42908  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
42909  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
42910  * @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
42911  * @cfg {Boolean}   closable      True if the panel can be closed/removed
42912  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
42913  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
42914  * @cfg {Toolbar}   toolbar       A toolbar for this panel
42915  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
42916  * @cfg {String} title          The title for this panel
42917  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
42918  * @cfg {String} url            Calls {@link #setUrl} with this value
42919  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
42920  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
42921  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
42922  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
42923  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
42924  * @cfg {Boolean} badges render the badges
42925  * @cfg {String} cls  extra classes to use  
42926  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
42927  
42928  * @constructor
42929  * Create a new ContentPanel.
42930  * @param {String/Object} config A string to set only the title or a config object
42931  
42932  */
42933 Roo.bootstrap.panel.Content = function( config){
42934     
42935     this.tpl = config.tpl || false;
42936     
42937     var el = config.el;
42938     var content = config.content;
42939
42940     if(config.autoCreate){ // xtype is available if this is called from factory
42941         el = Roo.id();
42942     }
42943     this.el = Roo.get(el);
42944     if(!this.el && config && config.autoCreate){
42945         if(typeof config.autoCreate == "object"){
42946             if(!config.autoCreate.id){
42947                 config.autoCreate.id = config.id||el;
42948             }
42949             this.el = Roo.DomHelper.append(document.body,
42950                         config.autoCreate, true);
42951         }else{
42952             var elcfg =  {
42953                 tag: "div",
42954                 cls: (config.cls || '') +
42955                     (config.background ? ' bg-' + config.background : '') +
42956                     " roo-layout-inactive-content",
42957                 id: config.id||el
42958             };
42959             if (config.iframe) {
42960                 elcfg.cn = [
42961                     {
42962                         tag : 'iframe',
42963                         style : 'border: 0px',
42964                         src : 'about:blank'
42965                     }
42966                 ];
42967             }
42968               
42969             if (config.html) {
42970                 elcfg.html = config.html;
42971                 
42972             }
42973                         
42974             this.el = Roo.DomHelper.append(document.body, elcfg , true);
42975             if (config.iframe) {
42976                 this.iframeEl = this.el.select('iframe',true).first();
42977             }
42978             
42979         }
42980     } 
42981     this.closable = false;
42982     this.loaded = false;
42983     this.active = false;
42984    
42985       
42986     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
42987         
42988         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
42989         
42990         this.wrapEl = this.el; //this.el.wrap();
42991         var ti = [];
42992         if (config.toolbar.items) {
42993             ti = config.toolbar.items ;
42994             delete config.toolbar.items ;
42995         }
42996         
42997         var nitems = [];
42998         this.toolbar.render(this.wrapEl, 'before');
42999         for(var i =0;i < ti.length;i++) {
43000           //  Roo.log(['add child', items[i]]);
43001             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43002         }
43003         this.toolbar.items = nitems;
43004         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
43005         delete config.toolbar;
43006         
43007     }
43008     /*
43009     // xtype created footer. - not sure if will work as we normally have to render first..
43010     if (this.footer && !this.footer.el && this.footer.xtype) {
43011         if (!this.wrapEl) {
43012             this.wrapEl = this.el.wrap();
43013         }
43014     
43015         this.footer.container = this.wrapEl.createChild();
43016          
43017         this.footer = Roo.factory(this.footer, Roo);
43018         
43019     }
43020     */
43021     
43022      if(typeof config == "string"){
43023         this.title = config;
43024     }else{
43025         Roo.apply(this, config);
43026     }
43027     
43028     if(this.resizeEl){
43029         this.resizeEl = Roo.get(this.resizeEl, true);
43030     }else{
43031         this.resizeEl = this.el;
43032     }
43033     // handle view.xtype
43034     
43035  
43036     
43037     
43038     this.addEvents({
43039         /**
43040          * @event activate
43041          * Fires when this panel is activated. 
43042          * @param {Roo.ContentPanel} this
43043          */
43044         "activate" : true,
43045         /**
43046          * @event deactivate
43047          * Fires when this panel is activated. 
43048          * @param {Roo.ContentPanel} this
43049          */
43050         "deactivate" : true,
43051
43052         /**
43053          * @event resize
43054          * Fires when this panel is resized if fitToFrame is true.
43055          * @param {Roo.ContentPanel} this
43056          * @param {Number} width The width after any component adjustments
43057          * @param {Number} height The height after any component adjustments
43058          */
43059         "resize" : true,
43060         
43061          /**
43062          * @event render
43063          * Fires when this tab is created
43064          * @param {Roo.ContentPanel} this
43065          */
43066         "render" : true,
43067         
43068           /**
43069          * @event scroll
43070          * Fires when this content is scrolled
43071          * @param {Roo.ContentPanel} this
43072          * @param {Event} scrollEvent
43073          */
43074         "scroll" : true
43075         
43076         
43077         
43078     });
43079     
43080
43081     
43082     
43083     if(this.autoScroll && !this.iframe){
43084         this.resizeEl.setStyle("overflow", "auto");
43085         this.resizeEl.on('scroll', this.onScroll, this);
43086     } else {
43087         // fix randome scrolling
43088         //this.el.on('scroll', function() {
43089         //    Roo.log('fix random scolling');
43090         //    this.scrollTo('top',0); 
43091         //});
43092     }
43093     content = content || this.content;
43094     if(content){
43095         this.setContent(content);
43096     }
43097     if(config && config.url){
43098         this.setUrl(this.url, this.params, this.loadOnce);
43099     }
43100     
43101     
43102     
43103     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
43104     
43105     if (this.view && typeof(this.view.xtype) != 'undefined') {
43106         this.view.el = this.el.appendChild(document.createElement("div"));
43107         this.view = Roo.factory(this.view); 
43108         this.view.render  &&  this.view.render(false, '');  
43109     }
43110     
43111     
43112     this.fireEvent('render', this);
43113 };
43114
43115 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
43116     
43117     cls : '',
43118     background : '',
43119     
43120     tabTip : '',
43121     
43122     iframe : false,
43123     iframeEl : false,
43124     
43125     /* Resize Element - use this to work out scroll etc. */
43126     resizeEl : false,
43127     
43128     setRegion : function(region){
43129         this.region = region;
43130         this.setActiveClass(region && !this.background);
43131     },
43132     
43133     
43134     setActiveClass: function(state)
43135     {
43136         if(state){
43137            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
43138            this.el.setStyle('position','relative');
43139         }else{
43140            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
43141            this.el.setStyle('position', 'absolute');
43142         } 
43143     },
43144     
43145     /**
43146      * Returns the toolbar for this Panel if one was configured. 
43147      * @return {Roo.Toolbar} 
43148      */
43149     getToolbar : function(){
43150         return this.toolbar;
43151     },
43152     
43153     setActiveState : function(active)
43154     {
43155         this.active = active;
43156         this.setActiveClass(active);
43157         if(!active){
43158             if(this.fireEvent("deactivate", this) === false){
43159                 return false;
43160             }
43161             return true;
43162         }
43163         this.fireEvent("activate", this);
43164         return true;
43165     },
43166     /**
43167      * Updates this panel's element (not for iframe)
43168      * @param {String} content The new content
43169      * @param {Boolean} loadScripts (optional) true to look for and process scripts
43170     */
43171     setContent : function(content, loadScripts){
43172         if (this.iframe) {
43173             return;
43174         }
43175         
43176         this.el.update(content, loadScripts);
43177     },
43178
43179     ignoreResize : function(w, h)
43180     {
43181         //return false; // always resize?
43182         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
43183             return true;
43184         }else{
43185             this.lastSize = {width: w, height: h};
43186             return false;
43187         }
43188     },
43189     /**
43190      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
43191      * @return {Roo.UpdateManager} The UpdateManager
43192      */
43193     getUpdateManager : function(){
43194         if (this.iframe) {
43195             return false;
43196         }
43197         return this.el.getUpdateManager();
43198     },
43199      /**
43200      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
43201      * Does not work with IFRAME contents
43202      * @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:
43203 <pre><code>
43204 panel.load({
43205     url: "your-url.php",
43206     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
43207     callback: yourFunction,
43208     scope: yourObject, //(optional scope)
43209     discardUrl: false,
43210     nocache: false,
43211     text: "Loading...",
43212     timeout: 30,
43213     scripts: false
43214 });
43215 </code></pre>
43216      
43217      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
43218      * 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.
43219      * @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}
43220      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
43221      * @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.
43222      * @return {Roo.ContentPanel} this
43223      */
43224     load : function(){
43225         
43226         if (this.iframe) {
43227             return this;
43228         }
43229         
43230         var um = this.el.getUpdateManager();
43231         um.update.apply(um, arguments);
43232         return this;
43233     },
43234
43235
43236     /**
43237      * 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.
43238      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
43239      * @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)
43240      * @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)
43241      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
43242      */
43243     setUrl : function(url, params, loadOnce){
43244         if (this.iframe) {
43245             this.iframeEl.dom.src = url;
43246             return false;
43247         }
43248         
43249         if(this.refreshDelegate){
43250             this.removeListener("activate", this.refreshDelegate);
43251         }
43252         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
43253         this.on("activate", this.refreshDelegate);
43254         return this.el.getUpdateManager();
43255     },
43256     
43257     _handleRefresh : function(url, params, loadOnce){
43258         if(!loadOnce || !this.loaded){
43259             var updater = this.el.getUpdateManager();
43260             updater.update(url, params, this._setLoaded.createDelegate(this));
43261         }
43262     },
43263     
43264     _setLoaded : function(){
43265         this.loaded = true;
43266     }, 
43267     
43268     /**
43269      * Returns this panel's id
43270      * @return {String} 
43271      */
43272     getId : function(){
43273         return this.el.id;
43274     },
43275     
43276     /** 
43277      * Returns this panel's element - used by regiosn to add.
43278      * @return {Roo.Element} 
43279      */
43280     getEl : function(){
43281         return this.wrapEl || this.el;
43282     },
43283     
43284    
43285     
43286     adjustForComponents : function(width, height)
43287     {
43288         //Roo.log('adjustForComponents ');
43289         if(this.resizeEl != this.el){
43290             width -= this.el.getFrameWidth('lr');
43291             height -= this.el.getFrameWidth('tb');
43292         }
43293         if(this.toolbar){
43294             var te = this.toolbar.getEl();
43295             te.setWidth(width);
43296             height -= te.getHeight();
43297         }
43298         if(this.footer){
43299             var te = this.footer.getEl();
43300             te.setWidth(width);
43301             height -= te.getHeight();
43302         }
43303         
43304         
43305         if(this.adjustments){
43306             width += this.adjustments[0];
43307             height += this.adjustments[1];
43308         }
43309         return {"width": width, "height": height};
43310     },
43311     
43312     setSize : function(width, height){
43313         if(this.fitToFrame && !this.ignoreResize(width, height)){
43314             if(this.fitContainer && this.resizeEl != this.el){
43315                 this.el.setSize(width, height);
43316             }
43317             var size = this.adjustForComponents(width, height);
43318             if (this.iframe) {
43319                 this.iframeEl.setSize(width,height);
43320             }
43321             
43322             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
43323             this.fireEvent('resize', this, size.width, size.height);
43324             
43325             
43326         }
43327     },
43328     
43329     /**
43330      * Returns this panel's title
43331      * @return {String} 
43332      */
43333     getTitle : function(){
43334         
43335         if (typeof(this.title) != 'object') {
43336             return this.title;
43337         }
43338         
43339         var t = '';
43340         for (var k in this.title) {
43341             if (!this.title.hasOwnProperty(k)) {
43342                 continue;
43343             }
43344             
43345             if (k.indexOf('-') >= 0) {
43346                 var s = k.split('-');
43347                 for (var i = 0; i<s.length; i++) {
43348                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
43349                 }
43350             } else {
43351                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
43352             }
43353         }
43354         return t;
43355     },
43356     
43357     /**
43358      * Set this panel's title
43359      * @param {String} title
43360      */
43361     setTitle : function(title){
43362         this.title = title;
43363         if(this.region){
43364             this.region.updatePanelTitle(this, title);
43365         }
43366     },
43367     
43368     /**
43369      * Returns true is this panel was configured to be closable
43370      * @return {Boolean} 
43371      */
43372     isClosable : function(){
43373         return this.closable;
43374     },
43375     
43376     beforeSlide : function(){
43377         this.el.clip();
43378         this.resizeEl.clip();
43379     },
43380     
43381     afterSlide : function(){
43382         this.el.unclip();
43383         this.resizeEl.unclip();
43384     },
43385     
43386     /**
43387      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
43388      *   Will fail silently if the {@link #setUrl} method has not been called.
43389      *   This does not activate the panel, just updates its content.
43390      */
43391     refresh : function(){
43392         if(this.refreshDelegate){
43393            this.loaded = false;
43394            this.refreshDelegate();
43395         }
43396     },
43397     
43398     /**
43399      * Destroys this panel
43400      */
43401     destroy : function(){
43402         this.el.removeAllListeners();
43403         var tempEl = document.createElement("span");
43404         tempEl.appendChild(this.el.dom);
43405         tempEl.innerHTML = "";
43406         this.el.remove();
43407         this.el = null;
43408     },
43409     
43410     /**
43411      * form - if the content panel contains a form - this is a reference to it.
43412      * @type {Roo.form.Form}
43413      */
43414     form : false,
43415     /**
43416      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
43417      *    This contains a reference to it.
43418      * @type {Roo.View}
43419      */
43420     view : false,
43421     
43422       /**
43423      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
43424      * <pre><code>
43425
43426 layout.addxtype({
43427        xtype : 'Form',
43428        items: [ .... ]
43429    }
43430 );
43431
43432 </code></pre>
43433      * @param {Object} cfg Xtype definition of item to add.
43434      */
43435     
43436     
43437     getChildContainer: function () {
43438         return this.getEl();
43439     },
43440     
43441     
43442     onScroll : function(e)
43443     {
43444         this.fireEvent('scroll', this, e);
43445     }
43446     
43447     
43448     /*
43449         var  ret = new Roo.factory(cfg);
43450         return ret;
43451         
43452         
43453         // add form..
43454         if (cfg.xtype.match(/^Form$/)) {
43455             
43456             var el;
43457             //if (this.footer) {
43458             //    el = this.footer.container.insertSibling(false, 'before');
43459             //} else {
43460                 el = this.el.createChild();
43461             //}
43462
43463             this.form = new  Roo.form.Form(cfg);
43464             
43465             
43466             if ( this.form.allItems.length) {
43467                 this.form.render(el.dom);
43468             }
43469             return this.form;
43470         }
43471         // should only have one of theses..
43472         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
43473             // views.. should not be just added - used named prop 'view''
43474             
43475             cfg.el = this.el.appendChild(document.createElement("div"));
43476             // factory?
43477             
43478             var ret = new Roo.factory(cfg);
43479              
43480              ret.render && ret.render(false, ''); // render blank..
43481             this.view = ret;
43482             return ret;
43483         }
43484         return false;
43485     }
43486     \*/
43487 });
43488  
43489 /**
43490  * @class Roo.bootstrap.panel.Grid
43491  * @extends Roo.bootstrap.panel.Content
43492  * @constructor
43493  * Create a new GridPanel.
43494  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
43495  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
43496  * @param {Object} config A the config object
43497   
43498  */
43499
43500
43501
43502 Roo.bootstrap.panel.Grid = function(config)
43503 {
43504     
43505       
43506     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
43507         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
43508
43509     config.el = this.wrapper;
43510     //this.el = this.wrapper;
43511     
43512       if (config.container) {
43513         // ctor'ed from a Border/panel.grid
43514         
43515         
43516         this.wrapper.setStyle("overflow", "hidden");
43517         this.wrapper.addClass('roo-grid-container');
43518
43519     }
43520     
43521     
43522     if(config.toolbar){
43523         var tool_el = this.wrapper.createChild();    
43524         this.toolbar = Roo.factory(config.toolbar);
43525         var ti = [];
43526         if (config.toolbar.items) {
43527             ti = config.toolbar.items ;
43528             delete config.toolbar.items ;
43529         }
43530         
43531         var nitems = [];
43532         this.toolbar.render(tool_el);
43533         for(var i =0;i < ti.length;i++) {
43534           //  Roo.log(['add child', items[i]]);
43535             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43536         }
43537         this.toolbar.items = nitems;
43538         
43539         delete config.toolbar;
43540     }
43541     
43542     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
43543     config.grid.scrollBody = true;;
43544     config.grid.monitorWindowResize = false; // turn off autosizing
43545     config.grid.autoHeight = false;
43546     config.grid.autoWidth = false;
43547     
43548     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
43549     
43550     if (config.background) {
43551         // render grid on panel activation (if panel background)
43552         this.on('activate', function(gp) {
43553             if (!gp.grid.rendered) {
43554                 gp.grid.render(this.wrapper);
43555                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
43556             }
43557         });
43558             
43559     } else {
43560         this.grid.render(this.wrapper);
43561         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
43562
43563     }
43564     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
43565     // ??? needed ??? config.el = this.wrapper;
43566     
43567     
43568     
43569   
43570     // xtype created footer. - not sure if will work as we normally have to render first..
43571     if (this.footer && !this.footer.el && this.footer.xtype) {
43572         
43573         var ctr = this.grid.getView().getFooterPanel(true);
43574         this.footer.dataSource = this.grid.dataSource;
43575         this.footer = Roo.factory(this.footer, Roo);
43576         this.footer.render(ctr);
43577         
43578     }
43579     
43580     
43581     
43582     
43583      
43584 };
43585
43586 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
43587 {
43588   
43589     getId : function(){
43590         return this.grid.id;
43591     },
43592     
43593     /**
43594      * Returns the grid for this panel
43595      * @return {Roo.bootstrap.Table} 
43596      */
43597     getGrid : function(){
43598         return this.grid;    
43599     },
43600     
43601     setSize : function(width, height)
43602     {
43603      
43604         //if(!this.ignoreResize(width, height)){
43605             var grid = this.grid;
43606             var size = this.adjustForComponents(width, height);
43607             // tfoot is not a footer?
43608           
43609             
43610             var gridel = grid.getGridEl();
43611             gridel.setSize(size.width, size.height);
43612             
43613             var tbd = grid.getGridEl().select('tbody', true).first();
43614             var thd = grid.getGridEl().select('thead',true).first();
43615             var tbf= grid.getGridEl().select('tfoot', true).first();
43616
43617             if (tbf) {
43618                 size.height -= tbf.getHeight();
43619             }
43620             if (thd) {
43621                 size.height -= thd.getHeight();
43622             }
43623             
43624             tbd.setSize(size.width, size.height );
43625             // this is for the account management tab -seems to work there.
43626             var thd = grid.getGridEl().select('thead',true).first();
43627             //if (tbd) {
43628             //    tbd.setSize(size.width, size.height - thd.getHeight());
43629             //}
43630              
43631             grid.autoSize();
43632         //}
43633    
43634     },
43635      
43636     
43637     
43638     beforeSlide : function(){
43639         this.grid.getView().scroller.clip();
43640     },
43641     
43642     afterSlide : function(){
43643         this.grid.getView().scroller.unclip();
43644     },
43645     
43646     destroy : function(){
43647         this.grid.destroy();
43648         delete this.grid;
43649         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
43650     }
43651 });
43652
43653 /**
43654  * @class Roo.bootstrap.panel.Nest
43655  * @extends Roo.bootstrap.panel.Content
43656  * @constructor
43657  * Create a new Panel, that can contain a layout.Border.
43658  * 
43659  * 
43660  * @param {String/Object} config A string to set only the title or a config object
43661  */
43662 Roo.bootstrap.panel.Nest = function(config)
43663 {
43664     // construct with only one argument..
43665     /* FIXME - implement nicer consturctors
43666     if (layout.layout) {
43667         config = layout;
43668         layout = config.layout;
43669         delete config.layout;
43670     }
43671     if (layout.xtype && !layout.getEl) {
43672         // then layout needs constructing..
43673         layout = Roo.factory(layout, Roo);
43674     }
43675     */
43676     
43677     config.el =  config.layout.getEl();
43678     
43679     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
43680     
43681     config.layout.monitorWindowResize = false; // turn off autosizing
43682     this.layout = config.layout;
43683     this.layout.getEl().addClass("roo-layout-nested-layout");
43684     this.layout.parent = this;
43685     
43686     
43687     
43688     
43689 };
43690
43691 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
43692     /**
43693     * @cfg {Roo.BorderLayout} layout The layout for this panel
43694     */
43695     layout : false,
43696
43697     setSize : function(width, height){
43698         if(!this.ignoreResize(width, height)){
43699             var size = this.adjustForComponents(width, height);
43700             var el = this.layout.getEl();
43701             if (size.height < 1) {
43702                 el.setWidth(size.width);   
43703             } else {
43704                 el.setSize(size.width, size.height);
43705             }
43706             var touch = el.dom.offsetWidth;
43707             this.layout.layout();
43708             // ie requires a double layout on the first pass
43709             if(Roo.isIE && !this.initialized){
43710                 this.initialized = true;
43711                 this.layout.layout();
43712             }
43713         }
43714     },
43715     
43716     // activate all subpanels if not currently active..
43717     
43718     setActiveState : function(active){
43719         this.active = active;
43720         this.setActiveClass(active);
43721         
43722         if(!active){
43723             this.fireEvent("deactivate", this);
43724             return;
43725         }
43726         
43727         this.fireEvent("activate", this);
43728         // not sure if this should happen before or after..
43729         if (!this.layout) {
43730             return; // should not happen..
43731         }
43732         var reg = false;
43733         for (var r in this.layout.regions) {
43734             reg = this.layout.getRegion(r);
43735             if (reg.getActivePanel()) {
43736                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
43737                 reg.setActivePanel(reg.getActivePanel());
43738                 continue;
43739             }
43740             if (!reg.panels.length) {
43741                 continue;
43742             }
43743             reg.showPanel(reg.getPanel(0));
43744         }
43745         
43746         
43747         
43748         
43749     },
43750     
43751     /**
43752      * Returns the nested BorderLayout for this panel
43753      * @return {Roo.BorderLayout} 
43754      */
43755     getLayout : function(){
43756         return this.layout;
43757     },
43758     
43759      /**
43760      * Adds a xtype elements to the layout of the nested panel
43761      * <pre><code>
43762
43763 panel.addxtype({
43764        xtype : 'ContentPanel',
43765        region: 'west',
43766        items: [ .... ]
43767    }
43768 );
43769
43770 panel.addxtype({
43771         xtype : 'NestedLayoutPanel',
43772         region: 'west',
43773         layout: {
43774            center: { },
43775            west: { }   
43776         },
43777         items : [ ... list of content panels or nested layout panels.. ]
43778    }
43779 );
43780 </code></pre>
43781      * @param {Object} cfg Xtype definition of item to add.
43782      */
43783     addxtype : function(cfg) {
43784         return this.layout.addxtype(cfg);
43785     
43786     }
43787 });/*
43788  * Based on:
43789  * Ext JS Library 1.1.1
43790  * Copyright(c) 2006-2007, Ext JS, LLC.
43791  *
43792  * Originally Released Under LGPL - original licence link has changed is not relivant.
43793  *
43794  * Fork - LGPL
43795  * <script type="text/javascript">
43796  */
43797 /**
43798  * @class Roo.TabPanel
43799  * @extends Roo.util.Observable
43800  * A lightweight tab container.
43801  * <br><br>
43802  * Usage:
43803  * <pre><code>
43804 // basic tabs 1, built from existing content
43805 var tabs = new Roo.TabPanel("tabs1");
43806 tabs.addTab("script", "View Script");
43807 tabs.addTab("markup", "View Markup");
43808 tabs.activate("script");
43809
43810 // more advanced tabs, built from javascript
43811 var jtabs = new Roo.TabPanel("jtabs");
43812 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
43813
43814 // set up the UpdateManager
43815 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
43816 var updater = tab2.getUpdateManager();
43817 updater.setDefaultUrl("ajax1.htm");
43818 tab2.on('activate', updater.refresh, updater, true);
43819
43820 // Use setUrl for Ajax loading
43821 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
43822 tab3.setUrl("ajax2.htm", null, true);
43823
43824 // Disabled tab
43825 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
43826 tab4.disable();
43827
43828 jtabs.activate("jtabs-1");
43829  * </code></pre>
43830  * @constructor
43831  * Create a new TabPanel.
43832  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
43833  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
43834  */
43835 Roo.bootstrap.panel.Tabs = function(config){
43836     /**
43837     * The container element for this TabPanel.
43838     * @type Roo.Element
43839     */
43840     this.el = Roo.get(config.el);
43841     delete config.el;
43842     if(config){
43843         if(typeof config == "boolean"){
43844             this.tabPosition = config ? "bottom" : "top";
43845         }else{
43846             Roo.apply(this, config);
43847         }
43848     }
43849     
43850     if(this.tabPosition == "bottom"){
43851         // if tabs are at the bottom = create the body first.
43852         this.bodyEl = Roo.get(this.createBody(this.el.dom));
43853         this.el.addClass("roo-tabs-bottom");
43854     }
43855     // next create the tabs holders
43856     
43857     if (this.tabPosition == "west"){
43858         
43859         var reg = this.region; // fake it..
43860         while (reg) {
43861             if (!reg.mgr.parent) {
43862                 break;
43863             }
43864             reg = reg.mgr.parent.region;
43865         }
43866         Roo.log("got nest?");
43867         Roo.log(reg);
43868         if (reg.mgr.getRegion('west')) {
43869             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
43870             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
43871             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
43872             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
43873             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
43874         
43875             
43876         }
43877         
43878         
43879     } else {
43880      
43881         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
43882         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
43883         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
43884         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
43885     }
43886     
43887     
43888     if(Roo.isIE){
43889         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
43890     }
43891     
43892     // finally - if tabs are at the top, then create the body last..
43893     if(this.tabPosition != "bottom"){
43894         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
43895          * @type Roo.Element
43896          */
43897         this.bodyEl = Roo.get(this.createBody(this.el.dom));
43898         this.el.addClass("roo-tabs-top");
43899     }
43900     this.items = [];
43901
43902     this.bodyEl.setStyle("position", "relative");
43903
43904     this.active = null;
43905     this.activateDelegate = this.activate.createDelegate(this);
43906
43907     this.addEvents({
43908         /**
43909          * @event tabchange
43910          * Fires when the active tab changes
43911          * @param {Roo.TabPanel} this
43912          * @param {Roo.TabPanelItem} activePanel The new active tab
43913          */
43914         "tabchange": true,
43915         /**
43916          * @event beforetabchange
43917          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
43918          * @param {Roo.TabPanel} this
43919          * @param {Object} e Set cancel to true on this object to cancel the tab change
43920          * @param {Roo.TabPanelItem} tab The tab being changed to
43921          */
43922         "beforetabchange" : true
43923     });
43924
43925     Roo.EventManager.onWindowResize(this.onResize, this);
43926     this.cpad = this.el.getPadding("lr");
43927     this.hiddenCount = 0;
43928
43929
43930     // toolbar on the tabbar support...
43931     if (this.toolbar) {
43932         alert("no toolbar support yet");
43933         this.toolbar  = false;
43934         /*
43935         var tcfg = this.toolbar;
43936         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
43937         this.toolbar = new Roo.Toolbar(tcfg);
43938         if (Roo.isSafari) {
43939             var tbl = tcfg.container.child('table', true);
43940             tbl.setAttribute('width', '100%');
43941         }
43942         */
43943         
43944     }
43945    
43946
43947
43948     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
43949 };
43950
43951 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
43952     /*
43953      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
43954      */
43955     tabPosition : "top",
43956     /*
43957      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
43958      */
43959     currentTabWidth : 0,
43960     /*
43961      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
43962      */
43963     minTabWidth : 40,
43964     /*
43965      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
43966      */
43967     maxTabWidth : 250,
43968     /*
43969      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
43970      */
43971     preferredTabWidth : 175,
43972     /*
43973      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
43974      */
43975     resizeTabs : false,
43976     /*
43977      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
43978      */
43979     monitorResize : true,
43980     /*
43981      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
43982      */
43983     toolbar : false,  // set by caller..
43984     
43985     region : false, /// set by caller
43986     
43987     disableTooltips : true, // not used yet...
43988
43989     /**
43990      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
43991      * @param {String} id The id of the div to use <b>or create</b>
43992      * @param {String} text The text for the tab
43993      * @param {String} content (optional) Content to put in the TabPanelItem body
43994      * @param {Boolean} closable (optional) True to create a close icon on the tab
43995      * @return {Roo.TabPanelItem} The created TabPanelItem
43996      */
43997     addTab : function(id, text, content, closable, tpl)
43998     {
43999         var item = new Roo.bootstrap.panel.TabItem({
44000             panel: this,
44001             id : id,
44002             text : text,
44003             closable : closable,
44004             tpl : tpl
44005         });
44006         this.addTabItem(item);
44007         if(content){
44008             item.setContent(content);
44009         }
44010         return item;
44011     },
44012
44013     /**
44014      * Returns the {@link Roo.TabPanelItem} with the specified id/index
44015      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
44016      * @return {Roo.TabPanelItem}
44017      */
44018     getTab : function(id){
44019         return this.items[id];
44020     },
44021
44022     /**
44023      * Hides the {@link Roo.TabPanelItem} with the specified id/index
44024      * @param {String/Number} id The id or index of the TabPanelItem to hide.
44025      */
44026     hideTab : function(id){
44027         var t = this.items[id];
44028         if(!t.isHidden()){
44029            t.setHidden(true);
44030            this.hiddenCount++;
44031            this.autoSizeTabs();
44032         }
44033     },
44034
44035     /**
44036      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
44037      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
44038      */
44039     unhideTab : function(id){
44040         var t = this.items[id];
44041         if(t.isHidden()){
44042            t.setHidden(false);
44043            this.hiddenCount--;
44044            this.autoSizeTabs();
44045         }
44046     },
44047
44048     /**
44049      * Adds an existing {@link Roo.TabPanelItem}.
44050      * @param {Roo.TabPanelItem} item The TabPanelItem to add
44051      */
44052     addTabItem : function(item)
44053     {
44054         this.items[item.id] = item;
44055         this.items.push(item);
44056         this.autoSizeTabs();
44057       //  if(this.resizeTabs){
44058     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
44059   //         this.autoSizeTabs();
44060 //        }else{
44061 //            item.autoSize();
44062        // }
44063     },
44064
44065     /**
44066      * Removes a {@link Roo.TabPanelItem}.
44067      * @param {String/Number} id The id or index of the TabPanelItem to remove.
44068      */
44069     removeTab : function(id){
44070         var items = this.items;
44071         var tab = items[id];
44072         if(!tab) { return; }
44073         var index = items.indexOf(tab);
44074         if(this.active == tab && items.length > 1){
44075             var newTab = this.getNextAvailable(index);
44076             if(newTab) {
44077                 newTab.activate();
44078             }
44079         }
44080         this.stripEl.dom.removeChild(tab.pnode.dom);
44081         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
44082             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
44083         }
44084         items.splice(index, 1);
44085         delete this.items[tab.id];
44086         tab.fireEvent("close", tab);
44087         tab.purgeListeners();
44088         this.autoSizeTabs();
44089     },
44090
44091     getNextAvailable : function(start){
44092         var items = this.items;
44093         var index = start;
44094         // look for a next tab that will slide over to
44095         // replace the one being removed
44096         while(index < items.length){
44097             var item = items[++index];
44098             if(item && !item.isHidden()){
44099                 return item;
44100             }
44101         }
44102         // if one isn't found select the previous tab (on the left)
44103         index = start;
44104         while(index >= 0){
44105             var item = items[--index];
44106             if(item && !item.isHidden()){
44107                 return item;
44108             }
44109         }
44110         return null;
44111     },
44112
44113     /**
44114      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
44115      * @param {String/Number} id The id or index of the TabPanelItem to disable.
44116      */
44117     disableTab : function(id){
44118         var tab = this.items[id];
44119         if(tab && this.active != tab){
44120             tab.disable();
44121         }
44122     },
44123
44124     /**
44125      * Enables a {@link Roo.TabPanelItem} that is disabled.
44126      * @param {String/Number} id The id or index of the TabPanelItem to enable.
44127      */
44128     enableTab : function(id){
44129         var tab = this.items[id];
44130         tab.enable();
44131     },
44132
44133     /**
44134      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
44135      * @param {String/Number} id The id or index of the TabPanelItem to activate.
44136      * @return {Roo.TabPanelItem} The TabPanelItem.
44137      */
44138     activate : function(id)
44139     {
44140         //Roo.log('activite:'  + id);
44141         
44142         var tab = this.items[id];
44143         if(!tab){
44144             return null;
44145         }
44146         if(tab == this.active || tab.disabled){
44147             return tab;
44148         }
44149         var e = {};
44150         this.fireEvent("beforetabchange", this, e, tab);
44151         if(e.cancel !== true && !tab.disabled){
44152             if(this.active){
44153                 this.active.hide();
44154             }
44155             this.active = this.items[id];
44156             this.active.show();
44157             this.fireEvent("tabchange", this, this.active);
44158         }
44159         return tab;
44160     },
44161
44162     /**
44163      * Gets the active {@link Roo.TabPanelItem}.
44164      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
44165      */
44166     getActiveTab : function(){
44167         return this.active;
44168     },
44169
44170     /**
44171      * Updates the tab body element to fit the height of the container element
44172      * for overflow scrolling
44173      * @param {Number} targetHeight (optional) Override the starting height from the elements height
44174      */
44175     syncHeight : function(targetHeight){
44176         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44177         var bm = this.bodyEl.getMargins();
44178         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
44179         this.bodyEl.setHeight(newHeight);
44180         return newHeight;
44181     },
44182
44183     onResize : function(){
44184         if(this.monitorResize){
44185             this.autoSizeTabs();
44186         }
44187     },
44188
44189     /**
44190      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
44191      */
44192     beginUpdate : function(){
44193         this.updating = true;
44194     },
44195
44196     /**
44197      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
44198      */
44199     endUpdate : function(){
44200         this.updating = false;
44201         this.autoSizeTabs();
44202     },
44203
44204     /**
44205      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
44206      */
44207     autoSizeTabs : function()
44208     {
44209         var count = this.items.length;
44210         var vcount = count - this.hiddenCount;
44211         
44212         if (vcount < 2) {
44213             this.stripEl.hide();
44214         } else {
44215             this.stripEl.show();
44216         }
44217         
44218         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
44219             return;
44220         }
44221         
44222         
44223         var w = Math.max(this.el.getWidth() - this.cpad, 10);
44224         var availWidth = Math.floor(w / vcount);
44225         var b = this.stripBody;
44226         if(b.getWidth() > w){
44227             var tabs = this.items;
44228             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
44229             if(availWidth < this.minTabWidth){
44230                 /*if(!this.sleft){    // incomplete scrolling code
44231                     this.createScrollButtons();
44232                 }
44233                 this.showScroll();
44234                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
44235             }
44236         }else{
44237             if(this.currentTabWidth < this.preferredTabWidth){
44238                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
44239             }
44240         }
44241     },
44242
44243     /**
44244      * Returns the number of tabs in this TabPanel.
44245      * @return {Number}
44246      */
44247      getCount : function(){
44248          return this.items.length;
44249      },
44250
44251     /**
44252      * Resizes all the tabs to the passed width
44253      * @param {Number} The new width
44254      */
44255     setTabWidth : function(width){
44256         this.currentTabWidth = width;
44257         for(var i = 0, len = this.items.length; i < len; i++) {
44258                 if(!this.items[i].isHidden()) {
44259                 this.items[i].setWidth(width);
44260             }
44261         }
44262     },
44263
44264     /**
44265      * Destroys this TabPanel
44266      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
44267      */
44268     destroy : function(removeEl){
44269         Roo.EventManager.removeResizeListener(this.onResize, this);
44270         for(var i = 0, len = this.items.length; i < len; i++){
44271             this.items[i].purgeListeners();
44272         }
44273         if(removeEl === true){
44274             this.el.update("");
44275             this.el.remove();
44276         }
44277     },
44278     
44279     createStrip : function(container)
44280     {
44281         var strip = document.createElement("nav");
44282         strip.className = Roo.bootstrap.version == 4 ?
44283             "navbar-light bg-light" : 
44284             "navbar navbar-default"; //"x-tabs-wrap";
44285         container.appendChild(strip);
44286         return strip;
44287     },
44288     
44289     createStripList : function(strip)
44290     {
44291         // div wrapper for retard IE
44292         // returns the "tr" element.
44293         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
44294         //'<div class="x-tabs-strip-wrap">'+
44295           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
44296           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
44297         return strip.firstChild; //.firstChild.firstChild.firstChild;
44298     },
44299     createBody : function(container)
44300     {
44301         var body = document.createElement("div");
44302         Roo.id(body, "tab-body");
44303         //Roo.fly(body).addClass("x-tabs-body");
44304         Roo.fly(body).addClass("tab-content");
44305         container.appendChild(body);
44306         return body;
44307     },
44308     createItemBody :function(bodyEl, id){
44309         var body = Roo.getDom(id);
44310         if(!body){
44311             body = document.createElement("div");
44312             body.id = id;
44313         }
44314         //Roo.fly(body).addClass("x-tabs-item-body");
44315         Roo.fly(body).addClass("tab-pane");
44316          bodyEl.insertBefore(body, bodyEl.firstChild);
44317         return body;
44318     },
44319     /** @private */
44320     createStripElements :  function(stripEl, text, closable, tpl)
44321     {
44322         var td = document.createElement("li"); // was td..
44323         td.className = 'nav-item';
44324         
44325         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
44326         
44327         
44328         stripEl.appendChild(td);
44329         /*if(closable){
44330             td.className = "x-tabs-closable";
44331             if(!this.closeTpl){
44332                 this.closeTpl = new Roo.Template(
44333                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44334                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
44335                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
44336                 );
44337             }
44338             var el = this.closeTpl.overwrite(td, {"text": text});
44339             var close = el.getElementsByTagName("div")[0];
44340             var inner = el.getElementsByTagName("em")[0];
44341             return {"el": el, "close": close, "inner": inner};
44342         } else {
44343         */
44344         // not sure what this is..
44345 //            if(!this.tabTpl){
44346                 //this.tabTpl = new Roo.Template(
44347                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44348                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
44349                 //);
44350 //                this.tabTpl = new Roo.Template(
44351 //                   '<a href="#">' +
44352 //                   '<span unselectable="on"' +
44353 //                            (this.disableTooltips ? '' : ' title="{text}"') +
44354 //                            ' >{text}</span></a>'
44355 //                );
44356 //                
44357 //            }
44358
44359
44360             var template = tpl || this.tabTpl || false;
44361             
44362             if(!template){
44363                 template =  new Roo.Template(
44364                         Roo.bootstrap.version == 4 ? 
44365                             (
44366                                 '<a class="nav-link" href="#" unselectable="on"' +
44367                                      (this.disableTooltips ? '' : ' title="{text}"') +
44368                                      ' >{text}</a>'
44369                             ) : (
44370                                 '<a class="nav-link" href="#">' +
44371                                 '<span unselectable="on"' +
44372                                          (this.disableTooltips ? '' : ' title="{text}"') +
44373                                     ' >{text}</span></a>'
44374                             )
44375                 );
44376             }
44377             
44378             switch (typeof(template)) {
44379                 case 'object' :
44380                     break;
44381                 case 'string' :
44382                     template = new Roo.Template(template);
44383                     break;
44384                 default :
44385                     break;
44386             }
44387             
44388             var el = template.overwrite(td, {"text": text});
44389             
44390             var inner = el.getElementsByTagName("span")[0];
44391             
44392             return {"el": el, "inner": inner};
44393             
44394     }
44395         
44396     
44397 });
44398
44399 /**
44400  * @class Roo.TabPanelItem
44401  * @extends Roo.util.Observable
44402  * Represents an individual item (tab plus body) in a TabPanel.
44403  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
44404  * @param {String} id The id of this TabPanelItem
44405  * @param {String} text The text for the tab of this TabPanelItem
44406  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
44407  */
44408 Roo.bootstrap.panel.TabItem = function(config){
44409     /**
44410      * The {@link Roo.TabPanel} this TabPanelItem belongs to
44411      * @type Roo.TabPanel
44412      */
44413     this.tabPanel = config.panel;
44414     /**
44415      * The id for this TabPanelItem
44416      * @type String
44417      */
44418     this.id = config.id;
44419     /** @private */
44420     this.disabled = false;
44421     /** @private */
44422     this.text = config.text;
44423     /** @private */
44424     this.loaded = false;
44425     this.closable = config.closable;
44426
44427     /**
44428      * The body element for this TabPanelItem.
44429      * @type Roo.Element
44430      */
44431     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
44432     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
44433     this.bodyEl.setStyle("display", "block");
44434     this.bodyEl.setStyle("zoom", "1");
44435     //this.hideAction();
44436
44437     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
44438     /** @private */
44439     this.el = Roo.get(els.el);
44440     this.inner = Roo.get(els.inner, true);
44441      this.textEl = Roo.bootstrap.version == 4 ?
44442         this.el : Roo.get(this.el.dom.firstChild, true);
44443
44444     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
44445     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
44446
44447     
44448 //    this.el.on("mousedown", this.onTabMouseDown, this);
44449     this.el.on("click", this.onTabClick, this);
44450     /** @private */
44451     if(config.closable){
44452         var c = Roo.get(els.close, true);
44453         c.dom.title = this.closeText;
44454         c.addClassOnOver("close-over");
44455         c.on("click", this.closeClick, this);
44456      }
44457
44458     this.addEvents({
44459          /**
44460          * @event activate
44461          * Fires when this tab becomes the active tab.
44462          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44463          * @param {Roo.TabPanelItem} this
44464          */
44465         "activate": true,
44466         /**
44467          * @event beforeclose
44468          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
44469          * @param {Roo.TabPanelItem} this
44470          * @param {Object} e Set cancel to true on this object to cancel the close.
44471          */
44472         "beforeclose": true,
44473         /**
44474          * @event close
44475          * Fires when this tab is closed.
44476          * @param {Roo.TabPanelItem} this
44477          */
44478          "close": true,
44479         /**
44480          * @event deactivate
44481          * Fires when this tab is no longer the active tab.
44482          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44483          * @param {Roo.TabPanelItem} this
44484          */
44485          "deactivate" : true
44486     });
44487     this.hidden = false;
44488
44489     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
44490 };
44491
44492 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
44493            {
44494     purgeListeners : function(){
44495        Roo.util.Observable.prototype.purgeListeners.call(this);
44496        this.el.removeAllListeners();
44497     },
44498     /**
44499      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
44500      */
44501     show : function(){
44502         this.status_node.addClass("active");
44503         this.showAction();
44504         if(Roo.isOpera){
44505             this.tabPanel.stripWrap.repaint();
44506         }
44507         this.fireEvent("activate", this.tabPanel, this);
44508     },
44509
44510     /**
44511      * Returns true if this tab is the active tab.
44512      * @return {Boolean}
44513      */
44514     isActive : function(){
44515         return this.tabPanel.getActiveTab() == this;
44516     },
44517
44518     /**
44519      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
44520      */
44521     hide : function(){
44522         this.status_node.removeClass("active");
44523         this.hideAction();
44524         this.fireEvent("deactivate", this.tabPanel, this);
44525     },
44526
44527     hideAction : function(){
44528         this.bodyEl.hide();
44529         this.bodyEl.setStyle("position", "absolute");
44530         this.bodyEl.setLeft("-20000px");
44531         this.bodyEl.setTop("-20000px");
44532     },
44533
44534     showAction : function(){
44535         this.bodyEl.setStyle("position", "relative");
44536         this.bodyEl.setTop("");
44537         this.bodyEl.setLeft("");
44538         this.bodyEl.show();
44539     },
44540
44541     /**
44542      * Set the tooltip for the tab.
44543      * @param {String} tooltip The tab's tooltip
44544      */
44545     setTooltip : function(text){
44546         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
44547             this.textEl.dom.qtip = text;
44548             this.textEl.dom.removeAttribute('title');
44549         }else{
44550             this.textEl.dom.title = text;
44551         }
44552     },
44553
44554     onTabClick : function(e){
44555         e.preventDefault();
44556         this.tabPanel.activate(this.id);
44557     },
44558
44559     onTabMouseDown : function(e){
44560         e.preventDefault();
44561         this.tabPanel.activate(this.id);
44562     },
44563 /*
44564     getWidth : function(){
44565         return this.inner.getWidth();
44566     },
44567
44568     setWidth : function(width){
44569         var iwidth = width - this.linode.getPadding("lr");
44570         this.inner.setWidth(iwidth);
44571         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
44572         this.linode.setWidth(width);
44573     },
44574 */
44575     /**
44576      * Show or hide the tab
44577      * @param {Boolean} hidden True to hide or false to show.
44578      */
44579     setHidden : function(hidden){
44580         this.hidden = hidden;
44581         this.linode.setStyle("display", hidden ? "none" : "");
44582     },
44583
44584     /**
44585      * Returns true if this tab is "hidden"
44586      * @return {Boolean}
44587      */
44588     isHidden : function(){
44589         return this.hidden;
44590     },
44591
44592     /**
44593      * Returns the text for this tab
44594      * @return {String}
44595      */
44596     getText : function(){
44597         return this.text;
44598     },
44599     /*
44600     autoSize : function(){
44601         //this.el.beginMeasure();
44602         this.textEl.setWidth(1);
44603         /*
44604          *  #2804 [new] Tabs in Roojs
44605          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
44606          */
44607         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
44608         //this.el.endMeasure();
44609     //},
44610
44611     /**
44612      * Sets the text for the tab (Note: this also sets the tooltip text)
44613      * @param {String} text The tab's text and tooltip
44614      */
44615     setText : function(text){
44616         this.text = text;
44617         this.textEl.update(text);
44618         this.setTooltip(text);
44619         //if(!this.tabPanel.resizeTabs){
44620         //    this.autoSize();
44621         //}
44622     },
44623     /**
44624      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
44625      */
44626     activate : function(){
44627         this.tabPanel.activate(this.id);
44628     },
44629
44630     /**
44631      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
44632      */
44633     disable : function(){
44634         if(this.tabPanel.active != this){
44635             this.disabled = true;
44636             this.status_node.addClass("disabled");
44637         }
44638     },
44639
44640     /**
44641      * Enables this TabPanelItem if it was previously disabled.
44642      */
44643     enable : function(){
44644         this.disabled = false;
44645         this.status_node.removeClass("disabled");
44646     },
44647
44648     /**
44649      * Sets the content for this TabPanelItem.
44650      * @param {String} content The content
44651      * @param {Boolean} loadScripts true to look for and load scripts
44652      */
44653     setContent : function(content, loadScripts){
44654         this.bodyEl.update(content, loadScripts);
44655     },
44656
44657     /**
44658      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
44659      * @return {Roo.UpdateManager} The UpdateManager
44660      */
44661     getUpdateManager : function(){
44662         return this.bodyEl.getUpdateManager();
44663     },
44664
44665     /**
44666      * Set a URL to be used to load the content for this TabPanelItem.
44667      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
44668      * @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)
44669      * @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)
44670      * @return {Roo.UpdateManager} The UpdateManager
44671      */
44672     setUrl : function(url, params, loadOnce){
44673         if(this.refreshDelegate){
44674             this.un('activate', this.refreshDelegate);
44675         }
44676         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44677         this.on("activate", this.refreshDelegate);
44678         return this.bodyEl.getUpdateManager();
44679     },
44680
44681     /** @private */
44682     _handleRefresh : function(url, params, loadOnce){
44683         if(!loadOnce || !this.loaded){
44684             var updater = this.bodyEl.getUpdateManager();
44685             updater.update(url, params, this._setLoaded.createDelegate(this));
44686         }
44687     },
44688
44689     /**
44690      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
44691      *   Will fail silently if the setUrl method has not been called.
44692      *   This does not activate the panel, just updates its content.
44693      */
44694     refresh : function(){
44695         if(this.refreshDelegate){
44696            this.loaded = false;
44697            this.refreshDelegate();
44698         }
44699     },
44700
44701     /** @private */
44702     _setLoaded : function(){
44703         this.loaded = true;
44704     },
44705
44706     /** @private */
44707     closeClick : function(e){
44708         var o = {};
44709         e.stopEvent();
44710         this.fireEvent("beforeclose", this, o);
44711         if(o.cancel !== true){
44712             this.tabPanel.removeTab(this.id);
44713         }
44714     },
44715     /**
44716      * The text displayed in the tooltip for the close icon.
44717      * @type String
44718      */
44719     closeText : "Close this tab"
44720 });
44721 /**
44722 *    This script refer to:
44723 *    Title: International Telephone Input
44724 *    Author: Jack O'Connor
44725 *    Code version:  v12.1.12
44726 *    Availability: https://github.com/jackocnr/intl-tel-input.git
44727 **/
44728
44729 Roo.bootstrap.form.PhoneInputData = function() {
44730     var d = [
44731       [
44732         "Afghanistan (‫افغانستان‬‎)",
44733         "af",
44734         "93"
44735       ],
44736       [
44737         "Albania (Shqipëri)",
44738         "al",
44739         "355"
44740       ],
44741       [
44742         "Algeria (‫الجزائر‬‎)",
44743         "dz",
44744         "213"
44745       ],
44746       [
44747         "American Samoa",
44748         "as",
44749         "1684"
44750       ],
44751       [
44752         "Andorra",
44753         "ad",
44754         "376"
44755       ],
44756       [
44757         "Angola",
44758         "ao",
44759         "244"
44760       ],
44761       [
44762         "Anguilla",
44763         "ai",
44764         "1264"
44765       ],
44766       [
44767         "Antigua and Barbuda",
44768         "ag",
44769         "1268"
44770       ],
44771       [
44772         "Argentina",
44773         "ar",
44774         "54"
44775       ],
44776       [
44777         "Armenia (Հայաստան)",
44778         "am",
44779         "374"
44780       ],
44781       [
44782         "Aruba",
44783         "aw",
44784         "297"
44785       ],
44786       [
44787         "Australia",
44788         "au",
44789         "61",
44790         0
44791       ],
44792       [
44793         "Austria (Österreich)",
44794         "at",
44795         "43"
44796       ],
44797       [
44798         "Azerbaijan (Azərbaycan)",
44799         "az",
44800         "994"
44801       ],
44802       [
44803         "Bahamas",
44804         "bs",
44805         "1242"
44806       ],
44807       [
44808         "Bahrain (‫البحرين‬‎)",
44809         "bh",
44810         "973"
44811       ],
44812       [
44813         "Bangladesh (বাংলাদেশ)",
44814         "bd",
44815         "880"
44816       ],
44817       [
44818         "Barbados",
44819         "bb",
44820         "1246"
44821       ],
44822       [
44823         "Belarus (Беларусь)",
44824         "by",
44825         "375"
44826       ],
44827       [
44828         "Belgium (België)",
44829         "be",
44830         "32"
44831       ],
44832       [
44833         "Belize",
44834         "bz",
44835         "501"
44836       ],
44837       [
44838         "Benin (Bénin)",
44839         "bj",
44840         "229"
44841       ],
44842       [
44843         "Bermuda",
44844         "bm",
44845         "1441"
44846       ],
44847       [
44848         "Bhutan (འབྲུག)",
44849         "bt",
44850         "975"
44851       ],
44852       [
44853         "Bolivia",
44854         "bo",
44855         "591"
44856       ],
44857       [
44858         "Bosnia and Herzegovina (Босна и Херцеговина)",
44859         "ba",
44860         "387"
44861       ],
44862       [
44863         "Botswana",
44864         "bw",
44865         "267"
44866       ],
44867       [
44868         "Brazil (Brasil)",
44869         "br",
44870         "55"
44871       ],
44872       [
44873         "British Indian Ocean Territory",
44874         "io",
44875         "246"
44876       ],
44877       [
44878         "British Virgin Islands",
44879         "vg",
44880         "1284"
44881       ],
44882       [
44883         "Brunei",
44884         "bn",
44885         "673"
44886       ],
44887       [
44888         "Bulgaria (България)",
44889         "bg",
44890         "359"
44891       ],
44892       [
44893         "Burkina Faso",
44894         "bf",
44895         "226"
44896       ],
44897       [
44898         "Burundi (Uburundi)",
44899         "bi",
44900         "257"
44901       ],
44902       [
44903         "Cambodia (កម្ពុជា)",
44904         "kh",
44905         "855"
44906       ],
44907       [
44908         "Cameroon (Cameroun)",
44909         "cm",
44910         "237"
44911       ],
44912       [
44913         "Canada",
44914         "ca",
44915         "1",
44916         1,
44917         ["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"]
44918       ],
44919       [
44920         "Cape Verde (Kabu Verdi)",
44921         "cv",
44922         "238"
44923       ],
44924       [
44925         "Caribbean Netherlands",
44926         "bq",
44927         "599",
44928         1
44929       ],
44930       [
44931         "Cayman Islands",
44932         "ky",
44933         "1345"
44934       ],
44935       [
44936         "Central African Republic (République centrafricaine)",
44937         "cf",
44938         "236"
44939       ],
44940       [
44941         "Chad (Tchad)",
44942         "td",
44943         "235"
44944       ],
44945       [
44946         "Chile",
44947         "cl",
44948         "56"
44949       ],
44950       [
44951         "China (中国)",
44952         "cn",
44953         "86"
44954       ],
44955       [
44956         "Christmas Island",
44957         "cx",
44958         "61",
44959         2
44960       ],
44961       [
44962         "Cocos (Keeling) Islands",
44963         "cc",
44964         "61",
44965         1
44966       ],
44967       [
44968         "Colombia",
44969         "co",
44970         "57"
44971       ],
44972       [
44973         "Comoros (‫جزر القمر‬‎)",
44974         "km",
44975         "269"
44976       ],
44977       [
44978         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
44979         "cd",
44980         "243"
44981       ],
44982       [
44983         "Congo (Republic) (Congo-Brazzaville)",
44984         "cg",
44985         "242"
44986       ],
44987       [
44988         "Cook Islands",
44989         "ck",
44990         "682"
44991       ],
44992       [
44993         "Costa Rica",
44994         "cr",
44995         "506"
44996       ],
44997       [
44998         "Côte d’Ivoire",
44999         "ci",
45000         "225"
45001       ],
45002       [
45003         "Croatia (Hrvatska)",
45004         "hr",
45005         "385"
45006       ],
45007       [
45008         "Cuba",
45009         "cu",
45010         "53"
45011       ],
45012       [
45013         "Curaçao",
45014         "cw",
45015         "599",
45016         0
45017       ],
45018       [
45019         "Cyprus (Κύπρος)",
45020         "cy",
45021         "357"
45022       ],
45023       [
45024         "Czech Republic (Česká republika)",
45025         "cz",
45026         "420"
45027       ],
45028       [
45029         "Denmark (Danmark)",
45030         "dk",
45031         "45"
45032       ],
45033       [
45034         "Djibouti",
45035         "dj",
45036         "253"
45037       ],
45038       [
45039         "Dominica",
45040         "dm",
45041         "1767"
45042       ],
45043       [
45044         "Dominican Republic (República Dominicana)",
45045         "do",
45046         "1",
45047         2,
45048         ["809", "829", "849"]
45049       ],
45050       [
45051         "Ecuador",
45052         "ec",
45053         "593"
45054       ],
45055       [
45056         "Egypt (‫مصر‬‎)",
45057         "eg",
45058         "20"
45059       ],
45060       [
45061         "El Salvador",
45062         "sv",
45063         "503"
45064       ],
45065       [
45066         "Equatorial Guinea (Guinea Ecuatorial)",
45067         "gq",
45068         "240"
45069       ],
45070       [
45071         "Eritrea",
45072         "er",
45073         "291"
45074       ],
45075       [
45076         "Estonia (Eesti)",
45077         "ee",
45078         "372"
45079       ],
45080       [
45081         "Ethiopia",
45082         "et",
45083         "251"
45084       ],
45085       [
45086         "Falkland Islands (Islas Malvinas)",
45087         "fk",
45088         "500"
45089       ],
45090       [
45091         "Faroe Islands (Føroyar)",
45092         "fo",
45093         "298"
45094       ],
45095       [
45096         "Fiji",
45097         "fj",
45098         "679"
45099       ],
45100       [
45101         "Finland (Suomi)",
45102         "fi",
45103         "358",
45104         0
45105       ],
45106       [
45107         "France",
45108         "fr",
45109         "33"
45110       ],
45111       [
45112         "French Guiana (Guyane française)",
45113         "gf",
45114         "594"
45115       ],
45116       [
45117         "French Polynesia (Polynésie française)",
45118         "pf",
45119         "689"
45120       ],
45121       [
45122         "Gabon",
45123         "ga",
45124         "241"
45125       ],
45126       [
45127         "Gambia",
45128         "gm",
45129         "220"
45130       ],
45131       [
45132         "Georgia (საქართველო)",
45133         "ge",
45134         "995"
45135       ],
45136       [
45137         "Germany (Deutschland)",
45138         "de",
45139         "49"
45140       ],
45141       [
45142         "Ghana (Gaana)",
45143         "gh",
45144         "233"
45145       ],
45146       [
45147         "Gibraltar",
45148         "gi",
45149         "350"
45150       ],
45151       [
45152         "Greece (Ελλάδα)",
45153         "gr",
45154         "30"
45155       ],
45156       [
45157         "Greenland (Kalaallit Nunaat)",
45158         "gl",
45159         "299"
45160       ],
45161       [
45162         "Grenada",
45163         "gd",
45164         "1473"
45165       ],
45166       [
45167         "Guadeloupe",
45168         "gp",
45169         "590",
45170         0
45171       ],
45172       [
45173         "Guam",
45174         "gu",
45175         "1671"
45176       ],
45177       [
45178         "Guatemala",
45179         "gt",
45180         "502"
45181       ],
45182       [
45183         "Guernsey",
45184         "gg",
45185         "44",
45186         1
45187       ],
45188       [
45189         "Guinea (Guinée)",
45190         "gn",
45191         "224"
45192       ],
45193       [
45194         "Guinea-Bissau (Guiné Bissau)",
45195         "gw",
45196         "245"
45197       ],
45198       [
45199         "Guyana",
45200         "gy",
45201         "592"
45202       ],
45203       [
45204         "Haiti",
45205         "ht",
45206         "509"
45207       ],
45208       [
45209         "Honduras",
45210         "hn",
45211         "504"
45212       ],
45213       [
45214         "Hong Kong (香港)",
45215         "hk",
45216         "852"
45217       ],
45218       [
45219         "Hungary (Magyarország)",
45220         "hu",
45221         "36"
45222       ],
45223       [
45224         "Iceland (Ísland)",
45225         "is",
45226         "354"
45227       ],
45228       [
45229         "India (भारत)",
45230         "in",
45231         "91"
45232       ],
45233       [
45234         "Indonesia",
45235         "id",
45236         "62"
45237       ],
45238       [
45239         "Iran (‫ایران‬‎)",
45240         "ir",
45241         "98"
45242       ],
45243       [
45244         "Iraq (‫العراق‬‎)",
45245         "iq",
45246         "964"
45247       ],
45248       [
45249         "Ireland",
45250         "ie",
45251         "353"
45252       ],
45253       [
45254         "Isle of Man",
45255         "im",
45256         "44",
45257         2
45258       ],
45259       [
45260         "Israel (‫ישראל‬‎)",
45261         "il",
45262         "972"
45263       ],
45264       [
45265         "Italy (Italia)",
45266         "it",
45267         "39",
45268         0
45269       ],
45270       [
45271         "Jamaica",
45272         "jm",
45273         "1876"
45274       ],
45275       [
45276         "Japan (日本)",
45277         "jp",
45278         "81"
45279       ],
45280       [
45281         "Jersey",
45282         "je",
45283         "44",
45284         3
45285       ],
45286       [
45287         "Jordan (‫الأردن‬‎)",
45288         "jo",
45289         "962"
45290       ],
45291       [
45292         "Kazakhstan (Казахстан)",
45293         "kz",
45294         "7",
45295         1
45296       ],
45297       [
45298         "Kenya",
45299         "ke",
45300         "254"
45301       ],
45302       [
45303         "Kiribati",
45304         "ki",
45305         "686"
45306       ],
45307       [
45308         "Kosovo",
45309         "xk",
45310         "383"
45311       ],
45312       [
45313         "Kuwait (‫الكويت‬‎)",
45314         "kw",
45315         "965"
45316       ],
45317       [
45318         "Kyrgyzstan (Кыргызстан)",
45319         "kg",
45320         "996"
45321       ],
45322       [
45323         "Laos (ລາວ)",
45324         "la",
45325         "856"
45326       ],
45327       [
45328         "Latvia (Latvija)",
45329         "lv",
45330         "371"
45331       ],
45332       [
45333         "Lebanon (‫لبنان‬‎)",
45334         "lb",
45335         "961"
45336       ],
45337       [
45338         "Lesotho",
45339         "ls",
45340         "266"
45341       ],
45342       [
45343         "Liberia",
45344         "lr",
45345         "231"
45346       ],
45347       [
45348         "Libya (‫ليبيا‬‎)",
45349         "ly",
45350         "218"
45351       ],
45352       [
45353         "Liechtenstein",
45354         "li",
45355         "423"
45356       ],
45357       [
45358         "Lithuania (Lietuva)",
45359         "lt",
45360         "370"
45361       ],
45362       [
45363         "Luxembourg",
45364         "lu",
45365         "352"
45366       ],
45367       [
45368         "Macau (澳門)",
45369         "mo",
45370         "853"
45371       ],
45372       [
45373         "Macedonia (FYROM) (Македонија)",
45374         "mk",
45375         "389"
45376       ],
45377       [
45378         "Madagascar (Madagasikara)",
45379         "mg",
45380         "261"
45381       ],
45382       [
45383         "Malawi",
45384         "mw",
45385         "265"
45386       ],
45387       [
45388         "Malaysia",
45389         "my",
45390         "60"
45391       ],
45392       [
45393         "Maldives",
45394         "mv",
45395         "960"
45396       ],
45397       [
45398         "Mali",
45399         "ml",
45400         "223"
45401       ],
45402       [
45403         "Malta",
45404         "mt",
45405         "356"
45406       ],
45407       [
45408         "Marshall Islands",
45409         "mh",
45410         "692"
45411       ],
45412       [
45413         "Martinique",
45414         "mq",
45415         "596"
45416       ],
45417       [
45418         "Mauritania (‫موريتانيا‬‎)",
45419         "mr",
45420         "222"
45421       ],
45422       [
45423         "Mauritius (Moris)",
45424         "mu",
45425         "230"
45426       ],
45427       [
45428         "Mayotte",
45429         "yt",
45430         "262",
45431         1
45432       ],
45433       [
45434         "Mexico (México)",
45435         "mx",
45436         "52"
45437       ],
45438       [
45439         "Micronesia",
45440         "fm",
45441         "691"
45442       ],
45443       [
45444         "Moldova (Republica Moldova)",
45445         "md",
45446         "373"
45447       ],
45448       [
45449         "Monaco",
45450         "mc",
45451         "377"
45452       ],
45453       [
45454         "Mongolia (Монгол)",
45455         "mn",
45456         "976"
45457       ],
45458       [
45459         "Montenegro (Crna Gora)",
45460         "me",
45461         "382"
45462       ],
45463       [
45464         "Montserrat",
45465         "ms",
45466         "1664"
45467       ],
45468       [
45469         "Morocco (‫المغرب‬‎)",
45470         "ma",
45471         "212",
45472         0
45473       ],
45474       [
45475         "Mozambique (Moçambique)",
45476         "mz",
45477         "258"
45478       ],
45479       [
45480         "Myanmar (Burma) (မြန်မာ)",
45481         "mm",
45482         "95"
45483       ],
45484       [
45485         "Namibia (Namibië)",
45486         "na",
45487         "264"
45488       ],
45489       [
45490         "Nauru",
45491         "nr",
45492         "674"
45493       ],
45494       [
45495         "Nepal (नेपाल)",
45496         "np",
45497         "977"
45498       ],
45499       [
45500         "Netherlands (Nederland)",
45501         "nl",
45502         "31"
45503       ],
45504       [
45505         "New Caledonia (Nouvelle-Calédonie)",
45506         "nc",
45507         "687"
45508       ],
45509       [
45510         "New Zealand",
45511         "nz",
45512         "64"
45513       ],
45514       [
45515         "Nicaragua",
45516         "ni",
45517         "505"
45518       ],
45519       [
45520         "Niger (Nijar)",
45521         "ne",
45522         "227"
45523       ],
45524       [
45525         "Nigeria",
45526         "ng",
45527         "234"
45528       ],
45529       [
45530         "Niue",
45531         "nu",
45532         "683"
45533       ],
45534       [
45535         "Norfolk Island",
45536         "nf",
45537         "672"
45538       ],
45539       [
45540         "North Korea (조선 민주주의 인민 공화국)",
45541         "kp",
45542         "850"
45543       ],
45544       [
45545         "Northern Mariana Islands",
45546         "mp",
45547         "1670"
45548       ],
45549       [
45550         "Norway (Norge)",
45551         "no",
45552         "47",
45553         0
45554       ],
45555       [
45556         "Oman (‫عُمان‬‎)",
45557         "om",
45558         "968"
45559       ],
45560       [
45561         "Pakistan (‫پاکستان‬‎)",
45562         "pk",
45563         "92"
45564       ],
45565       [
45566         "Palau",
45567         "pw",
45568         "680"
45569       ],
45570       [
45571         "Palestine (‫فلسطين‬‎)",
45572         "ps",
45573         "970"
45574       ],
45575       [
45576         "Panama (Panamá)",
45577         "pa",
45578         "507"
45579       ],
45580       [
45581         "Papua New Guinea",
45582         "pg",
45583         "675"
45584       ],
45585       [
45586         "Paraguay",
45587         "py",
45588         "595"
45589       ],
45590       [
45591         "Peru (Perú)",
45592         "pe",
45593         "51"
45594       ],
45595       [
45596         "Philippines",
45597         "ph",
45598         "63"
45599       ],
45600       [
45601         "Poland (Polska)",
45602         "pl",
45603         "48"
45604       ],
45605       [
45606         "Portugal",
45607         "pt",
45608         "351"
45609       ],
45610       [
45611         "Puerto Rico",
45612         "pr",
45613         "1",
45614         3,
45615         ["787", "939"]
45616       ],
45617       [
45618         "Qatar (‫قطر‬‎)",
45619         "qa",
45620         "974"
45621       ],
45622       [
45623         "Réunion (La Réunion)",
45624         "re",
45625         "262",
45626         0
45627       ],
45628       [
45629         "Romania (România)",
45630         "ro",
45631         "40"
45632       ],
45633       [
45634         "Russia (Россия)",
45635         "ru",
45636         "7",
45637         0
45638       ],
45639       [
45640         "Rwanda",
45641         "rw",
45642         "250"
45643       ],
45644       [
45645         "Saint Barthélemy",
45646         "bl",
45647         "590",
45648         1
45649       ],
45650       [
45651         "Saint Helena",
45652         "sh",
45653         "290"
45654       ],
45655       [
45656         "Saint Kitts and Nevis",
45657         "kn",
45658         "1869"
45659       ],
45660       [
45661         "Saint Lucia",
45662         "lc",
45663         "1758"
45664       ],
45665       [
45666         "Saint Martin (Saint-Martin (partie française))",
45667         "mf",
45668         "590",
45669         2
45670       ],
45671       [
45672         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
45673         "pm",
45674         "508"
45675       ],
45676       [
45677         "Saint Vincent and the Grenadines",
45678         "vc",
45679         "1784"
45680       ],
45681       [
45682         "Samoa",
45683         "ws",
45684         "685"
45685       ],
45686       [
45687         "San Marino",
45688         "sm",
45689         "378"
45690       ],
45691       [
45692         "São Tomé and Príncipe (São Tomé e Príncipe)",
45693         "st",
45694         "239"
45695       ],
45696       [
45697         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
45698         "sa",
45699         "966"
45700       ],
45701       [
45702         "Senegal (Sénégal)",
45703         "sn",
45704         "221"
45705       ],
45706       [
45707         "Serbia (Србија)",
45708         "rs",
45709         "381"
45710       ],
45711       [
45712         "Seychelles",
45713         "sc",
45714         "248"
45715       ],
45716       [
45717         "Sierra Leone",
45718         "sl",
45719         "232"
45720       ],
45721       [
45722         "Singapore",
45723         "sg",
45724         "65"
45725       ],
45726       [
45727         "Sint Maarten",
45728         "sx",
45729         "1721"
45730       ],
45731       [
45732         "Slovakia (Slovensko)",
45733         "sk",
45734         "421"
45735       ],
45736       [
45737         "Slovenia (Slovenija)",
45738         "si",
45739         "386"
45740       ],
45741       [
45742         "Solomon Islands",
45743         "sb",
45744         "677"
45745       ],
45746       [
45747         "Somalia (Soomaaliya)",
45748         "so",
45749         "252"
45750       ],
45751       [
45752         "South Africa",
45753         "za",
45754         "27"
45755       ],
45756       [
45757         "South Korea (대한민국)",
45758         "kr",
45759         "82"
45760       ],
45761       [
45762         "South Sudan (‫جنوب السودان‬‎)",
45763         "ss",
45764         "211"
45765       ],
45766       [
45767         "Spain (España)",
45768         "es",
45769         "34"
45770       ],
45771       [
45772         "Sri Lanka (ශ්‍රී ලංකාව)",
45773         "lk",
45774         "94"
45775       ],
45776       [
45777         "Sudan (‫السودان‬‎)",
45778         "sd",
45779         "249"
45780       ],
45781       [
45782         "Suriname",
45783         "sr",
45784         "597"
45785       ],
45786       [
45787         "Svalbard and Jan Mayen",
45788         "sj",
45789         "47",
45790         1
45791       ],
45792       [
45793         "Swaziland",
45794         "sz",
45795         "268"
45796       ],
45797       [
45798         "Sweden (Sverige)",
45799         "se",
45800         "46"
45801       ],
45802       [
45803         "Switzerland (Schweiz)",
45804         "ch",
45805         "41"
45806       ],
45807       [
45808         "Syria (‫سوريا‬‎)",
45809         "sy",
45810         "963"
45811       ],
45812       [
45813         "Taiwan (台灣)",
45814         "tw",
45815         "886"
45816       ],
45817       [
45818         "Tajikistan",
45819         "tj",
45820         "992"
45821       ],
45822       [
45823         "Tanzania",
45824         "tz",
45825         "255"
45826       ],
45827       [
45828         "Thailand (ไทย)",
45829         "th",
45830         "66"
45831       ],
45832       [
45833         "Timor-Leste",
45834         "tl",
45835         "670"
45836       ],
45837       [
45838         "Togo",
45839         "tg",
45840         "228"
45841       ],
45842       [
45843         "Tokelau",
45844         "tk",
45845         "690"
45846       ],
45847       [
45848         "Tonga",
45849         "to",
45850         "676"
45851       ],
45852       [
45853         "Trinidad and Tobago",
45854         "tt",
45855         "1868"
45856       ],
45857       [
45858         "Tunisia (‫تونس‬‎)",
45859         "tn",
45860         "216"
45861       ],
45862       [
45863         "Turkey (Türkiye)",
45864         "tr",
45865         "90"
45866       ],
45867       [
45868         "Turkmenistan",
45869         "tm",
45870         "993"
45871       ],
45872       [
45873         "Turks and Caicos Islands",
45874         "tc",
45875         "1649"
45876       ],
45877       [
45878         "Tuvalu",
45879         "tv",
45880         "688"
45881       ],
45882       [
45883         "U.S. Virgin Islands",
45884         "vi",
45885         "1340"
45886       ],
45887       [
45888         "Uganda",
45889         "ug",
45890         "256"
45891       ],
45892       [
45893         "Ukraine (Україна)",
45894         "ua",
45895         "380"
45896       ],
45897       [
45898         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
45899         "ae",
45900         "971"
45901       ],
45902       [
45903         "United Kingdom",
45904         "gb",
45905         "44",
45906         0
45907       ],
45908       [
45909         "United States",
45910         "us",
45911         "1",
45912         0
45913       ],
45914       [
45915         "Uruguay",
45916         "uy",
45917         "598"
45918       ],
45919       [
45920         "Uzbekistan (Oʻzbekiston)",
45921         "uz",
45922         "998"
45923       ],
45924       [
45925         "Vanuatu",
45926         "vu",
45927         "678"
45928       ],
45929       [
45930         "Vatican City (Città del Vaticano)",
45931         "va",
45932         "39",
45933         1
45934       ],
45935       [
45936         "Venezuela",
45937         "ve",
45938         "58"
45939       ],
45940       [
45941         "Vietnam (Việt Nam)",
45942         "vn",
45943         "84"
45944       ],
45945       [
45946         "Wallis and Futuna (Wallis-et-Futuna)",
45947         "wf",
45948         "681"
45949       ],
45950       [
45951         "Western Sahara (‫الصحراء الغربية‬‎)",
45952         "eh",
45953         "212",
45954         1
45955       ],
45956       [
45957         "Yemen (‫اليمن‬‎)",
45958         "ye",
45959         "967"
45960       ],
45961       [
45962         "Zambia",
45963         "zm",
45964         "260"
45965       ],
45966       [
45967         "Zimbabwe",
45968         "zw",
45969         "263"
45970       ],
45971       [
45972         "Åland Islands",
45973         "ax",
45974         "358",
45975         1
45976       ]
45977   ];
45978   
45979   return d;
45980 }/**
45981 *    This script refer to:
45982 *    Title: International Telephone Input
45983 *    Author: Jack O'Connor
45984 *    Code version:  v12.1.12
45985 *    Availability: https://github.com/jackocnr/intl-tel-input.git
45986 **/
45987
45988 /**
45989  * @class Roo.bootstrap.form.PhoneInput
45990  * @extends Roo.bootstrap.form.TriggerField
45991  * An input with International dial-code selection
45992  
45993  * @cfg {String} defaultDialCode default '+852'
45994  * @cfg {Array} preferedCountries default []
45995   
45996  * @constructor
45997  * Create a new PhoneInput.
45998  * @param {Object} config Configuration options
45999  */
46000
46001 Roo.bootstrap.form.PhoneInput = function(config) {
46002     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
46003 };
46004
46005 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
46006         /**
46007         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
46008         */
46009         listWidth: undefined,
46010         
46011         selectedClass: 'active',
46012         
46013         invalidClass : "has-warning",
46014         
46015         validClass: 'has-success',
46016         
46017         allowed: '0123456789',
46018         
46019         max_length: 15,
46020         
46021         /**
46022          * @cfg {String} defaultDialCode The default dial code when initializing the input
46023          */
46024         defaultDialCode: '+852',
46025         
46026         /**
46027          * @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
46028          */
46029         preferedCountries: false,
46030         
46031         getAutoCreate : function()
46032         {
46033             var data = Roo.bootstrap.form.PhoneInputData();
46034             var align = this.labelAlign || this.parentLabelAlign();
46035             var id = Roo.id();
46036             
46037             this.allCountries = [];
46038             this.dialCodeMapping = [];
46039             
46040             for (var i = 0; i < data.length; i++) {
46041               var c = data[i];
46042               this.allCountries[i] = {
46043                 name: c[0],
46044                 iso2: c[1],
46045                 dialCode: c[2],
46046                 priority: c[3] || 0,
46047                 areaCodes: c[4] || null
46048               };
46049               this.dialCodeMapping[c[2]] = {
46050                   name: c[0],
46051                   iso2: c[1],
46052                   priority: c[3] || 0,
46053                   areaCodes: c[4] || null
46054               };
46055             }
46056             
46057             var cfg = {
46058                 cls: 'form-group',
46059                 cn: []
46060             };
46061             
46062             var input =  {
46063                 tag: 'input',
46064                 id : id,
46065                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
46066                 maxlength: this.max_length,
46067                 cls : 'form-control tel-input',
46068                 autocomplete: 'new-password'
46069             };
46070             
46071             var hiddenInput = {
46072                 tag: 'input',
46073                 type: 'hidden',
46074                 cls: 'hidden-tel-input'
46075             };
46076             
46077             if (this.name) {
46078                 hiddenInput.name = this.name;
46079             }
46080             
46081             if (this.disabled) {
46082                 input.disabled = true;
46083             }
46084             
46085             var flag_container = {
46086                 tag: 'div',
46087                 cls: 'flag-box',
46088                 cn: [
46089                     {
46090                         tag: 'div',
46091                         cls: 'flag'
46092                     },
46093                     {
46094                         tag: 'div',
46095                         cls: 'caret'
46096                     }
46097                 ]
46098             };
46099             
46100             var box = {
46101                 tag: 'div',
46102                 cls: this.hasFeedback ? 'has-feedback' : '',
46103                 cn: [
46104                     hiddenInput,
46105                     input,
46106                     {
46107                         tag: 'input',
46108                         cls: 'dial-code-holder',
46109                         disabled: true
46110                     }
46111                 ]
46112             };
46113             
46114             var container = {
46115                 cls: 'roo-select2-container input-group',
46116                 cn: [
46117                     flag_container,
46118                     box
46119                 ]
46120             };
46121             
46122             if (this.fieldLabel.length) {
46123                 var indicator = {
46124                     tag: 'i',
46125                     tooltip: 'This field is required'
46126                 };
46127                 
46128                 var label = {
46129                     tag: 'label',
46130                     'for':  id,
46131                     cls: 'control-label',
46132                     cn: []
46133                 };
46134                 
46135                 var label_text = {
46136                     tag: 'span',
46137                     html: this.fieldLabel
46138                 };
46139                 
46140                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46141                 label.cn = [
46142                     indicator,
46143                     label_text
46144                 ];
46145                 
46146                 if(this.indicatorpos == 'right') {
46147                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46148                     label.cn = [
46149                         label_text,
46150                         indicator
46151                     ];
46152                 }
46153                 
46154                 if(align == 'left') {
46155                     container = {
46156                         tag: 'div',
46157                         cn: [
46158                             container
46159                         ]
46160                     };
46161                     
46162                     if(this.labelWidth > 12){
46163                         label.style = "width: " + this.labelWidth + 'px';
46164                     }
46165                     if(this.labelWidth < 13 && this.labelmd == 0){
46166                         this.labelmd = this.labelWidth;
46167                     }
46168                     if(this.labellg > 0){
46169                         label.cls += ' col-lg-' + this.labellg;
46170                         input.cls += ' col-lg-' + (12 - this.labellg);
46171                     }
46172                     if(this.labelmd > 0){
46173                         label.cls += ' col-md-' + this.labelmd;
46174                         container.cls += ' col-md-' + (12 - this.labelmd);
46175                     }
46176                     if(this.labelsm > 0){
46177                         label.cls += ' col-sm-' + this.labelsm;
46178                         container.cls += ' col-sm-' + (12 - this.labelsm);
46179                     }
46180                     if(this.labelxs > 0){
46181                         label.cls += ' col-xs-' + this.labelxs;
46182                         container.cls += ' col-xs-' + (12 - this.labelxs);
46183                     }
46184                 }
46185             }
46186             
46187             cfg.cn = [
46188                 label,
46189                 container
46190             ];
46191             
46192             var settings = this;
46193             
46194             ['xs','sm','md','lg'].map(function(size){
46195                 if (settings[size]) {
46196                     cfg.cls += ' col-' + size + '-' + settings[size];
46197                 }
46198             });
46199             
46200             this.store = new Roo.data.Store({
46201                 proxy : new Roo.data.MemoryProxy({}),
46202                 reader : new Roo.data.JsonReader({
46203                     fields : [
46204                         {
46205                             'name' : 'name',
46206                             'type' : 'string'
46207                         },
46208                         {
46209                             'name' : 'iso2',
46210                             'type' : 'string'
46211                         },
46212                         {
46213                             'name' : 'dialCode',
46214                             'type' : 'string'
46215                         },
46216                         {
46217                             'name' : 'priority',
46218                             'type' : 'string'
46219                         },
46220                         {
46221                             'name' : 'areaCodes',
46222                             'type' : 'string'
46223                         }
46224                     ]
46225                 })
46226             });
46227             
46228             if(!this.preferedCountries) {
46229                 this.preferedCountries = [
46230                     'hk',
46231                     'gb',
46232                     'us'
46233                 ];
46234             }
46235             
46236             var p = this.preferedCountries.reverse();
46237             
46238             if(p) {
46239                 for (var i = 0; i < p.length; i++) {
46240                     for (var j = 0; j < this.allCountries.length; j++) {
46241                         if(this.allCountries[j].iso2 == p[i]) {
46242                             var t = this.allCountries[j];
46243                             this.allCountries.splice(j,1);
46244                             this.allCountries.unshift(t);
46245                         }
46246                     } 
46247                 }
46248             }
46249             
46250             this.store.proxy.data = {
46251                 success: true,
46252                 data: this.allCountries
46253             };
46254             
46255             return cfg;
46256         },
46257         
46258         initEvents : function()
46259         {
46260             this.createList();
46261             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
46262             
46263             this.indicator = this.indicatorEl();
46264             this.flag = this.flagEl();
46265             this.dialCodeHolder = this.dialCodeHolderEl();
46266             
46267             this.trigger = this.el.select('div.flag-box',true).first();
46268             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
46269             
46270             var _this = this;
46271             
46272             (function(){
46273                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46274                 _this.list.setWidth(lw);
46275             }).defer(100);
46276             
46277             this.list.on('mouseover', this.onViewOver, this);
46278             this.list.on('mousemove', this.onViewMove, this);
46279             this.inputEl().on("keyup", this.onKeyUp, this);
46280             this.inputEl().on("keypress", this.onKeyPress, this);
46281             
46282             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
46283
46284             this.view = new Roo.View(this.list, this.tpl, {
46285                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
46286             });
46287             
46288             this.view.on('click', this.onViewClick, this);
46289             this.setValue(this.defaultDialCode);
46290         },
46291         
46292         onTriggerClick : function(e)
46293         {
46294             Roo.log('trigger click');
46295             if(this.disabled){
46296                 return;
46297             }
46298             
46299             if(this.isExpanded()){
46300                 this.collapse();
46301                 this.hasFocus = false;
46302             }else {
46303                 this.store.load({});
46304                 this.hasFocus = true;
46305                 this.expand();
46306             }
46307         },
46308         
46309         isExpanded : function()
46310         {
46311             return this.list.isVisible();
46312         },
46313         
46314         collapse : function()
46315         {
46316             if(!this.isExpanded()){
46317                 return;
46318             }
46319             this.list.hide();
46320             Roo.get(document).un('mousedown', this.collapseIf, this);
46321             Roo.get(document).un('mousewheel', this.collapseIf, this);
46322             this.fireEvent('collapse', this);
46323             this.validate();
46324         },
46325         
46326         expand : function()
46327         {
46328             Roo.log('expand');
46329
46330             if(this.isExpanded() || !this.hasFocus){
46331                 return;
46332             }
46333             
46334             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
46335             this.list.setWidth(lw);
46336             
46337             this.list.show();
46338             this.restrictHeight();
46339             
46340             Roo.get(document).on('mousedown', this.collapseIf, this);
46341             Roo.get(document).on('mousewheel', this.collapseIf, this);
46342             
46343             this.fireEvent('expand', this);
46344         },
46345         
46346         restrictHeight : function()
46347         {
46348             this.list.alignTo(this.inputEl(), this.listAlign);
46349             this.list.alignTo(this.inputEl(), this.listAlign);
46350         },
46351         
46352         onViewOver : function(e, t)
46353         {
46354             if(this.inKeyMode){
46355                 return;
46356             }
46357             var item = this.view.findItemFromChild(t);
46358             
46359             if(item){
46360                 var index = this.view.indexOf(item);
46361                 this.select(index, false);
46362             }
46363         },
46364
46365         // private
46366         onViewClick : function(view, doFocus, el, e)
46367         {
46368             var index = this.view.getSelectedIndexes()[0];
46369             
46370             var r = this.store.getAt(index);
46371             
46372             if(r){
46373                 this.onSelect(r, index);
46374             }
46375             if(doFocus !== false && !this.blockFocus){
46376                 this.inputEl().focus();
46377             }
46378         },
46379         
46380         onViewMove : function(e, t)
46381         {
46382             this.inKeyMode = false;
46383         },
46384         
46385         select : function(index, scrollIntoView)
46386         {
46387             this.selectedIndex = index;
46388             this.view.select(index);
46389             if(scrollIntoView !== false){
46390                 var el = this.view.getNode(index);
46391                 if(el){
46392                     this.list.scrollChildIntoView(el, false);
46393                 }
46394             }
46395         },
46396         
46397         createList : function()
46398         {
46399             this.list = Roo.get(document.body).createChild({
46400                 tag: 'ul',
46401                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
46402                 style: 'display:none'
46403             });
46404             
46405             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
46406         },
46407         
46408         collapseIf : function(e)
46409         {
46410             var in_combo  = e.within(this.el);
46411             var in_list =  e.within(this.list);
46412             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
46413             
46414             if (in_combo || in_list || is_list) {
46415                 return;
46416             }
46417             this.collapse();
46418         },
46419         
46420         onSelect : function(record, index)
46421         {
46422             if(this.fireEvent('beforeselect', this, record, index) !== false){
46423                 
46424                 this.setFlagClass(record.data.iso2);
46425                 this.setDialCode(record.data.dialCode);
46426                 this.hasFocus = false;
46427                 this.collapse();
46428                 this.fireEvent('select', this, record, index);
46429             }
46430         },
46431         
46432         flagEl : function()
46433         {
46434             var flag = this.el.select('div.flag',true).first();
46435             if(!flag){
46436                 return false;
46437             }
46438             return flag;
46439         },
46440         
46441         dialCodeHolderEl : function()
46442         {
46443             var d = this.el.select('input.dial-code-holder',true).first();
46444             if(!d){
46445                 return false;
46446             }
46447             return d;
46448         },
46449         
46450         setDialCode : function(v)
46451         {
46452             this.dialCodeHolder.dom.value = '+'+v;
46453         },
46454         
46455         setFlagClass : function(n)
46456         {
46457             this.flag.dom.className = 'flag '+n;
46458         },
46459         
46460         getValue : function()
46461         {
46462             var v = this.inputEl().getValue();
46463             if(this.dialCodeHolder) {
46464                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
46465             }
46466             return v;
46467         },
46468         
46469         setValue : function(v)
46470         {
46471             var d = this.getDialCode(v);
46472             
46473             //invalid dial code
46474             if(v.length == 0 || !d || d.length == 0) {
46475                 if(this.rendered){
46476                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
46477                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
46478                 }
46479                 return;
46480             }
46481             
46482             //valid dial code
46483             this.setFlagClass(this.dialCodeMapping[d].iso2);
46484             this.setDialCode(d);
46485             this.inputEl().dom.value = v.replace('+'+d,'');
46486             this.hiddenEl().dom.value = this.getValue();
46487             
46488             this.validate();
46489         },
46490         
46491         getDialCode : function(v)
46492         {
46493             v = v ||  '';
46494             
46495             if (v.length == 0) {
46496                 return this.dialCodeHolder.dom.value;
46497             }
46498             
46499             var dialCode = "";
46500             if (v.charAt(0) != "+") {
46501                 return false;
46502             }
46503             var numericChars = "";
46504             for (var i = 1; i < v.length; i++) {
46505               var c = v.charAt(i);
46506               if (!isNaN(c)) {
46507                 numericChars += c;
46508                 if (this.dialCodeMapping[numericChars]) {
46509                   dialCode = v.substr(1, i);
46510                 }
46511                 if (numericChars.length == 4) {
46512                   break;
46513                 }
46514               }
46515             }
46516             return dialCode;
46517         },
46518         
46519         reset : function()
46520         {
46521             this.setValue(this.defaultDialCode);
46522             this.validate();
46523         },
46524         
46525         hiddenEl : function()
46526         {
46527             return this.el.select('input.hidden-tel-input',true).first();
46528         },
46529         
46530         // after setting val
46531         onKeyUp : function(e){
46532             this.setValue(this.getValue());
46533         },
46534         
46535         onKeyPress : function(e){
46536             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
46537                 e.stopEvent();
46538             }
46539         }
46540         
46541 });
46542 /**
46543  * @class Roo.bootstrap.form.MoneyField
46544  * @extends Roo.bootstrap.form.ComboBox
46545  * Bootstrap MoneyField class
46546  * 
46547  * @constructor
46548  * Create a new MoneyField.
46549  * @param {Object} config Configuration options
46550  */
46551
46552 Roo.bootstrap.form.MoneyField = function(config) {
46553     
46554     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
46555     
46556 };
46557
46558 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
46559     
46560     /**
46561      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
46562      */
46563     allowDecimals : true,
46564     /**
46565      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
46566      */
46567     decimalSeparator : ".",
46568     /**
46569      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
46570      */
46571     decimalPrecision : 0,
46572     /**
46573      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
46574      */
46575     allowNegative : true,
46576     /**
46577      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
46578      */
46579     allowZero: true,
46580     /**
46581      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
46582      */
46583     minValue : Number.NEGATIVE_INFINITY,
46584     /**
46585      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
46586      */
46587     maxValue : Number.MAX_VALUE,
46588     /**
46589      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
46590      */
46591     minText : "The minimum value for this field is {0}",
46592     /**
46593      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
46594      */
46595     maxText : "The maximum value for this field is {0}",
46596     /**
46597      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
46598      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
46599      */
46600     nanText : "{0} is not a valid number",
46601     /**
46602      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
46603      */
46604     castInt : true,
46605     /**
46606      * @cfg {String} defaults currency of the MoneyField
46607      * value should be in lkey
46608      */
46609     defaultCurrency : false,
46610     /**
46611      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
46612      */
46613     thousandsDelimiter : false,
46614     /**
46615      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
46616      */
46617     max_length: false,
46618     
46619     inputlg : 9,
46620     inputmd : 9,
46621     inputsm : 9,
46622     inputxs : 6,
46623      /**
46624      * @cfg {Roo.data.Store} store  Store to lookup currency??
46625      */
46626     store : false,
46627     
46628     getAutoCreate : function()
46629     {
46630         var align = this.labelAlign || this.parentLabelAlign();
46631         
46632         var id = Roo.id();
46633
46634         var cfg = {
46635             cls: 'form-group',
46636             cn: []
46637         };
46638
46639         var input =  {
46640             tag: 'input',
46641             id : id,
46642             cls : 'form-control roo-money-amount-input',
46643             autocomplete: 'new-password'
46644         };
46645         
46646         var hiddenInput = {
46647             tag: 'input',
46648             type: 'hidden',
46649             id: Roo.id(),
46650             cls: 'hidden-number-input'
46651         };
46652         
46653         if(this.max_length) {
46654             input.maxlength = this.max_length; 
46655         }
46656         
46657         if (this.name) {
46658             hiddenInput.name = this.name;
46659         }
46660
46661         if (this.disabled) {
46662             input.disabled = true;
46663         }
46664
46665         var clg = 12 - this.inputlg;
46666         var cmd = 12 - this.inputmd;
46667         var csm = 12 - this.inputsm;
46668         var cxs = 12 - this.inputxs;
46669         
46670         var container = {
46671             tag : 'div',
46672             cls : 'row roo-money-field',
46673             cn : [
46674                 {
46675                     tag : 'div',
46676                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
46677                     cn : [
46678                         {
46679                             tag : 'div',
46680                             cls: 'roo-select2-container input-group',
46681                             cn: [
46682                                 {
46683                                     tag : 'input',
46684                                     cls : 'form-control roo-money-currency-input',
46685                                     autocomplete: 'new-password',
46686                                     readOnly : 1,
46687                                     name : this.currencyName
46688                                 },
46689                                 {
46690                                     tag :'span',
46691                                     cls : 'input-group-addon',
46692                                     cn : [
46693                                         {
46694                                             tag: 'span',
46695                                             cls: 'caret'
46696                                         }
46697                                     ]
46698                                 }
46699                             ]
46700                         }
46701                     ]
46702                 },
46703                 {
46704                     tag : 'div',
46705                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
46706                     cn : [
46707                         {
46708                             tag: 'div',
46709                             cls: this.hasFeedback ? 'has-feedback' : '',
46710                             cn: [
46711                                 input
46712                             ]
46713                         }
46714                     ]
46715                 }
46716             ]
46717             
46718         };
46719         
46720         if (this.fieldLabel.length) {
46721             var indicator = {
46722                 tag: 'i',
46723                 tooltip: 'This field is required'
46724             };
46725
46726             var label = {
46727                 tag: 'label',
46728                 'for':  id,
46729                 cls: 'control-label',
46730                 cn: []
46731             };
46732
46733             var label_text = {
46734                 tag: 'span',
46735                 html: this.fieldLabel
46736             };
46737
46738             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46739             label.cn = [
46740                 indicator,
46741                 label_text
46742             ];
46743
46744             if(this.indicatorpos == 'right') {
46745                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46746                 label.cn = [
46747                     label_text,
46748                     indicator
46749                 ];
46750             }
46751
46752             if(align == 'left') {
46753                 container = {
46754                     tag: 'div',
46755                     cn: [
46756                         container
46757                     ]
46758                 };
46759
46760                 if(this.labelWidth > 12){
46761                     label.style = "width: " + this.labelWidth + 'px';
46762                 }
46763                 if(this.labelWidth < 13 && this.labelmd == 0){
46764                     this.labelmd = this.labelWidth;
46765                 }
46766                 if(this.labellg > 0){
46767                     label.cls += ' col-lg-' + this.labellg;
46768                     input.cls += ' col-lg-' + (12 - this.labellg);
46769                 }
46770                 if(this.labelmd > 0){
46771                     label.cls += ' col-md-' + this.labelmd;
46772                     container.cls += ' col-md-' + (12 - this.labelmd);
46773                 }
46774                 if(this.labelsm > 0){
46775                     label.cls += ' col-sm-' + this.labelsm;
46776                     container.cls += ' col-sm-' + (12 - this.labelsm);
46777                 }
46778                 if(this.labelxs > 0){
46779                     label.cls += ' col-xs-' + this.labelxs;
46780                     container.cls += ' col-xs-' + (12 - this.labelxs);
46781                 }
46782             }
46783         }
46784
46785         cfg.cn = [
46786             label,
46787             container,
46788             hiddenInput
46789         ];
46790         
46791         var settings = this;
46792
46793         ['xs','sm','md','lg'].map(function(size){
46794             if (settings[size]) {
46795                 cfg.cls += ' col-' + size + '-' + settings[size];
46796             }
46797         });
46798         
46799         return cfg;
46800     },
46801     
46802     initEvents : function()
46803     {
46804         this.indicator = this.indicatorEl();
46805         
46806         this.initCurrencyEvent();
46807         
46808         this.initNumberEvent();
46809     },
46810     
46811     initCurrencyEvent : function()
46812     {
46813         if (!this.store) {
46814             throw "can not find store for combo";
46815         }
46816         
46817         this.store = Roo.factory(this.store, Roo.data);
46818         this.store.parent = this;
46819         
46820         this.createList();
46821         
46822         this.triggerEl = this.el.select('.input-group-addon', true).first();
46823         
46824         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
46825         
46826         var _this = this;
46827         
46828         (function(){
46829             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46830             _this.list.setWidth(lw);
46831         }).defer(100);
46832         
46833         this.list.on('mouseover', this.onViewOver, this);
46834         this.list.on('mousemove', this.onViewMove, this);
46835         this.list.on('scroll', this.onViewScroll, this);
46836         
46837         if(!this.tpl){
46838             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
46839         }
46840         
46841         this.view = new Roo.View(this.list, this.tpl, {
46842             singleSelect:true, store: this.store, selectedClass: this.selectedClass
46843         });
46844         
46845         this.view.on('click', this.onViewClick, this);
46846         
46847         this.store.on('beforeload', this.onBeforeLoad, this);
46848         this.store.on('load', this.onLoad, this);
46849         this.store.on('loadexception', this.onLoadException, this);
46850         
46851         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
46852             "up" : function(e){
46853                 this.inKeyMode = true;
46854                 this.selectPrev();
46855             },
46856
46857             "down" : function(e){
46858                 if(!this.isExpanded()){
46859                     this.onTriggerClick();
46860                 }else{
46861                     this.inKeyMode = true;
46862                     this.selectNext();
46863                 }
46864             },
46865
46866             "enter" : function(e){
46867                 this.collapse();
46868                 
46869                 if(this.fireEvent("specialkey", this, e)){
46870                     this.onViewClick(false);
46871                 }
46872                 
46873                 return true;
46874             },
46875
46876             "esc" : function(e){
46877                 this.collapse();
46878             },
46879
46880             "tab" : function(e){
46881                 this.collapse();
46882                 
46883                 if(this.fireEvent("specialkey", this, e)){
46884                     this.onViewClick(false);
46885                 }
46886                 
46887                 return true;
46888             },
46889
46890             scope : this,
46891
46892             doRelay : function(foo, bar, hname){
46893                 if(hname == 'down' || this.scope.isExpanded()){
46894                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
46895                 }
46896                 return true;
46897             },
46898
46899             forceKeyDown: true
46900         });
46901         
46902         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
46903         
46904     },
46905     
46906     initNumberEvent : function(e)
46907     {
46908         this.inputEl().on("keydown" , this.fireKey,  this);
46909         this.inputEl().on("focus", this.onFocus,  this);
46910         this.inputEl().on("blur", this.onBlur,  this);
46911         
46912         this.inputEl().relayEvent('keyup', this);
46913         
46914         if(this.indicator){
46915             this.indicator.addClass('invisible');
46916         }
46917  
46918         this.originalValue = this.getValue();
46919         
46920         if(this.validationEvent == 'keyup'){
46921             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
46922             this.inputEl().on('keyup', this.filterValidation, this);
46923         }
46924         else if(this.validationEvent !== false){
46925             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
46926         }
46927         
46928         if(this.selectOnFocus){
46929             this.on("focus", this.preFocus, this);
46930             
46931         }
46932         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
46933             this.inputEl().on("keypress", this.filterKeys, this);
46934         } else {
46935             this.inputEl().relayEvent('keypress', this);
46936         }
46937         
46938         var allowed = "0123456789";
46939         
46940         if(this.allowDecimals){
46941             allowed += this.decimalSeparator;
46942         }
46943         
46944         if(this.allowNegative){
46945             allowed += "-";
46946         }
46947         
46948         if(this.thousandsDelimiter) {
46949             allowed += ",";
46950         }
46951         
46952         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
46953         
46954         var keyPress = function(e){
46955             
46956             var k = e.getKey();
46957             
46958             var c = e.getCharCode();
46959             
46960             if(
46961                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
46962                     allowed.indexOf(String.fromCharCode(c)) === -1
46963             ){
46964                 e.stopEvent();
46965                 return;
46966             }
46967             
46968             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
46969                 return;
46970             }
46971             
46972             if(allowed.indexOf(String.fromCharCode(c)) === -1){
46973                 e.stopEvent();
46974             }
46975         };
46976         
46977         this.inputEl().on("keypress", keyPress, this);
46978         
46979     },
46980     
46981     onTriggerClick : function(e)
46982     {   
46983         if(this.disabled){
46984             return;
46985         }
46986         
46987         this.page = 0;
46988         this.loadNext = false;
46989         
46990         if(this.isExpanded()){
46991             this.collapse();
46992             return;
46993         }
46994         
46995         this.hasFocus = true;
46996         
46997         if(this.triggerAction == 'all') {
46998             this.doQuery(this.allQuery, true);
46999             return;
47000         }
47001         
47002         this.doQuery(this.getRawValue());
47003     },
47004     
47005     getCurrency : function()
47006     {   
47007         var v = this.currencyEl().getValue();
47008         
47009         return v;
47010     },
47011     
47012     restrictHeight : function()
47013     {
47014         this.list.alignTo(this.currencyEl(), this.listAlign);
47015         this.list.alignTo(this.currencyEl(), this.listAlign);
47016     },
47017     
47018     onViewClick : function(view, doFocus, el, e)
47019     {
47020         var index = this.view.getSelectedIndexes()[0];
47021         
47022         var r = this.store.getAt(index);
47023         
47024         if(r){
47025             this.onSelect(r, index);
47026         }
47027     },
47028     
47029     onSelect : function(record, index){
47030         
47031         if(this.fireEvent('beforeselect', this, record, index) !== false){
47032         
47033             this.setFromCurrencyData(index > -1 ? record.data : false);
47034             
47035             this.collapse();
47036             
47037             this.fireEvent('select', this, record, index);
47038         }
47039     },
47040     
47041     setFromCurrencyData : function(o)
47042     {
47043         var currency = '';
47044         
47045         this.lastCurrency = o;
47046         
47047         if (this.currencyField) {
47048             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
47049         } else {
47050             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
47051         }
47052         
47053         this.lastSelectionText = currency;
47054         
47055         //setting default currency
47056         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
47057             this.setCurrency(this.defaultCurrency);
47058             return;
47059         }
47060         
47061         this.setCurrency(currency);
47062     },
47063     
47064     setFromData : function(o)
47065     {
47066         var c = {};
47067         
47068         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
47069         
47070         this.setFromCurrencyData(c);
47071         
47072         var value = '';
47073         
47074         if (this.name) {
47075             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
47076         } else {
47077             Roo.log('no value set for '+ (this.name ? this.name : this.id));
47078         }
47079         
47080         this.setValue(value);
47081         
47082     },
47083     
47084     setCurrency : function(v)
47085     {   
47086         this.currencyValue = v;
47087         
47088         if(this.rendered){
47089             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
47090             this.validate();
47091         }
47092     },
47093     
47094     setValue : function(v)
47095     {
47096         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
47097         
47098         this.value = v;
47099         
47100         if(this.rendered){
47101             
47102             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47103             
47104             this.inputEl().dom.value = (v == '') ? '' :
47105                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
47106             
47107             if(!this.allowZero && v === '0') {
47108                 this.hiddenEl().dom.value = '';
47109                 this.inputEl().dom.value = '';
47110             }
47111             
47112             this.validate();
47113         }
47114     },
47115     
47116     getRawValue : function()
47117     {
47118         var v = this.inputEl().getValue();
47119         
47120         return v;
47121     },
47122     
47123     getValue : function()
47124     {
47125         return this.fixPrecision(this.parseValue(this.getRawValue()));
47126     },
47127     
47128     parseValue : function(value)
47129     {
47130         if(this.thousandsDelimiter) {
47131             value += "";
47132             r = new RegExp(",", "g");
47133             value = value.replace(r, "");
47134         }
47135         
47136         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
47137         return isNaN(value) ? '' : value;
47138         
47139     },
47140     
47141     fixPrecision : function(value)
47142     {
47143         if(this.thousandsDelimiter) {
47144             value += "";
47145             r = new RegExp(",", "g");
47146             value = value.replace(r, "");
47147         }
47148         
47149         var nan = isNaN(value);
47150         
47151         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
47152             return nan ? '' : value;
47153         }
47154         return parseFloat(value).toFixed(this.decimalPrecision);
47155     },
47156     
47157     decimalPrecisionFcn : function(v)
47158     {
47159         return Math.floor(v);
47160     },
47161     
47162     validateValue : function(value)
47163     {
47164         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
47165             return false;
47166         }
47167         
47168         var num = this.parseValue(value);
47169         
47170         if(isNaN(num)){
47171             this.markInvalid(String.format(this.nanText, value));
47172             return false;
47173         }
47174         
47175         if(num < this.minValue){
47176             this.markInvalid(String.format(this.minText, this.minValue));
47177             return false;
47178         }
47179         
47180         if(num > this.maxValue){
47181             this.markInvalid(String.format(this.maxText, this.maxValue));
47182             return false;
47183         }
47184         
47185         return true;
47186     },
47187     
47188     validate : function()
47189     {
47190         if(this.disabled || this.allowBlank){
47191             this.markValid();
47192             return true;
47193         }
47194         
47195         var currency = this.getCurrency();
47196         
47197         if(this.validateValue(this.getRawValue()) && currency.length){
47198             this.markValid();
47199             return true;
47200         }
47201         
47202         this.markInvalid();
47203         return false;
47204     },
47205     
47206     getName: function()
47207     {
47208         return this.name;
47209     },
47210     
47211     beforeBlur : function()
47212     {
47213         if(!this.castInt){
47214             return;
47215         }
47216         
47217         var v = this.parseValue(this.getRawValue());
47218         
47219         if(v || v == 0){
47220             this.setValue(v);
47221         }
47222     },
47223     
47224     onBlur : function()
47225     {
47226         this.beforeBlur();
47227         
47228         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
47229             //this.el.removeClass(this.focusClass);
47230         }
47231         
47232         this.hasFocus = false;
47233         
47234         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
47235             this.validate();
47236         }
47237         
47238         var v = this.getValue();
47239         
47240         if(String(v) !== String(this.startValue)){
47241             this.fireEvent('change', this, v, this.startValue);
47242         }
47243         
47244         this.fireEvent("blur", this);
47245     },
47246     
47247     inputEl : function()
47248     {
47249         return this.el.select('.roo-money-amount-input', true).first();
47250     },
47251     
47252     currencyEl : function()
47253     {
47254         return this.el.select('.roo-money-currency-input', true).first();
47255     },
47256     
47257     hiddenEl : function()
47258     {
47259         return this.el.select('input.hidden-number-input',true).first();
47260     }
47261     
47262 });/**
47263  * @class Roo.bootstrap.BezierSignature
47264  * @extends Roo.bootstrap.Component
47265  * Bootstrap BezierSignature class
47266  * This script refer to:
47267  *    Title: Signature Pad
47268  *    Author: szimek
47269  *    Availability: https://github.com/szimek/signature_pad
47270  *
47271  * @constructor
47272  * Create a new BezierSignature
47273  * @param {Object} config The config object
47274  */
47275
47276 Roo.bootstrap.BezierSignature = function(config){
47277     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
47278     this.addEvents({
47279         "resize" : true
47280     });
47281 };
47282
47283 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
47284 {
47285      
47286     curve_data: [],
47287     
47288     is_empty: true,
47289     
47290     mouse_btn_down: true,
47291     
47292     /**
47293      * @cfg {int} canvas height
47294      */
47295     canvas_height: '200px',
47296     
47297     /**
47298      * @cfg {float|function} Radius of a single dot.
47299      */ 
47300     dot_size: false,
47301     
47302     /**
47303      * @cfg {float} Minimum width of a line. Defaults to 0.5.
47304      */
47305     min_width: 0.5,
47306     
47307     /**
47308      * @cfg {float} Maximum width of a line. Defaults to 2.5.
47309      */
47310     max_width: 2.5,
47311     
47312     /**
47313      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
47314      */
47315     throttle: 16,
47316     
47317     /**
47318      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
47319      */
47320     min_distance: 5,
47321     
47322     /**
47323      * @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.
47324      */
47325     bg_color: 'rgba(0, 0, 0, 0)',
47326     
47327     /**
47328      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
47329      */
47330     dot_color: 'black',
47331     
47332     /**
47333      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
47334      */ 
47335     velocity_filter_weight: 0.7,
47336     
47337     /**
47338      * @cfg {function} Callback when stroke begin. 
47339      */
47340     onBegin: false,
47341     
47342     /**
47343      * @cfg {function} Callback when stroke end.
47344      */
47345     onEnd: false,
47346     
47347     getAutoCreate : function()
47348     {
47349         var cls = 'roo-signature column';
47350         
47351         if(this.cls){
47352             cls += ' ' + this.cls;
47353         }
47354         
47355         var col_sizes = [
47356             'lg',
47357             'md',
47358             'sm',
47359             'xs'
47360         ];
47361         
47362         for(var i = 0; i < col_sizes.length; i++) {
47363             if(this[col_sizes[i]]) {
47364                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
47365             }
47366         }
47367         
47368         var cfg = {
47369             tag: 'div',
47370             cls: cls,
47371             cn: [
47372                 {
47373                     tag: 'div',
47374                     cls: 'roo-signature-body',
47375                     cn: [
47376                         {
47377                             tag: 'canvas',
47378                             cls: 'roo-signature-body-canvas',
47379                             height: this.canvas_height,
47380                             width: this.canvas_width
47381                         }
47382                     ]
47383                 },
47384                 {
47385                     tag: 'input',
47386                     type: 'file',
47387                     style: 'display: none'
47388                 }
47389             ]
47390         };
47391         
47392         return cfg;
47393     },
47394     
47395     initEvents: function() 
47396     {
47397         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
47398         
47399         var canvas = this.canvasEl();
47400         
47401         // mouse && touch event swapping...
47402         canvas.dom.style.touchAction = 'none';
47403         canvas.dom.style.msTouchAction = 'none';
47404         
47405         this.mouse_btn_down = false;
47406         canvas.on('mousedown', this._handleMouseDown, this);
47407         canvas.on('mousemove', this._handleMouseMove, this);
47408         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
47409         
47410         if (window.PointerEvent) {
47411             canvas.on('pointerdown', this._handleMouseDown, this);
47412             canvas.on('pointermove', this._handleMouseMove, this);
47413             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
47414         }
47415         
47416         if ('ontouchstart' in window) {
47417             canvas.on('touchstart', this._handleTouchStart, this);
47418             canvas.on('touchmove', this._handleTouchMove, this);
47419             canvas.on('touchend', this._handleTouchEnd, this);
47420         }
47421         
47422         Roo.EventManager.onWindowResize(this.resize, this, true);
47423         
47424         // file input event
47425         this.fileEl().on('change', this.uploadImage, this);
47426         
47427         this.clear();
47428         
47429         this.resize();
47430     },
47431     
47432     resize: function(){
47433         
47434         var canvas = this.canvasEl().dom;
47435         var ctx = this.canvasElCtx();
47436         var img_data = false;
47437         
47438         if(canvas.width > 0) {
47439             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
47440         }
47441         // setting canvas width will clean img data
47442         canvas.width = 0;
47443         
47444         var style = window.getComputedStyle ? 
47445             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
47446             
47447         var padding_left = parseInt(style.paddingLeft) || 0;
47448         var padding_right = parseInt(style.paddingRight) || 0;
47449         
47450         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
47451         
47452         if(img_data) {
47453             ctx.putImageData(img_data, 0, 0);
47454         }
47455     },
47456     
47457     _handleMouseDown: function(e)
47458     {
47459         if (e.browserEvent.which === 1) {
47460             this.mouse_btn_down = true;
47461             this.strokeBegin(e);
47462         }
47463     },
47464     
47465     _handleMouseMove: function (e)
47466     {
47467         if (this.mouse_btn_down) {
47468             this.strokeMoveUpdate(e);
47469         }
47470     },
47471     
47472     _handleMouseUp: function (e)
47473     {
47474         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
47475             this.mouse_btn_down = false;
47476             this.strokeEnd(e);
47477         }
47478     },
47479     
47480     _handleTouchStart: function (e) {
47481         
47482         e.preventDefault();
47483         if (e.browserEvent.targetTouches.length === 1) {
47484             // var touch = e.browserEvent.changedTouches[0];
47485             // this.strokeBegin(touch);
47486             
47487              this.strokeBegin(e); // assume e catching the correct xy...
47488         }
47489     },
47490     
47491     _handleTouchMove: function (e) {
47492         e.preventDefault();
47493         // var touch = event.targetTouches[0];
47494         // _this._strokeMoveUpdate(touch);
47495         this.strokeMoveUpdate(e);
47496     },
47497     
47498     _handleTouchEnd: function (e) {
47499         var wasCanvasTouched = e.target === this.canvasEl().dom;
47500         if (wasCanvasTouched) {
47501             e.preventDefault();
47502             // var touch = event.changedTouches[0];
47503             // _this._strokeEnd(touch);
47504             this.strokeEnd(e);
47505         }
47506     },
47507     
47508     reset: function () {
47509         this._lastPoints = [];
47510         this._lastVelocity = 0;
47511         this._lastWidth = (this.min_width + this.max_width) / 2;
47512         this.canvasElCtx().fillStyle = this.dot_color;
47513     },
47514     
47515     strokeMoveUpdate: function(e)
47516     {
47517         this.strokeUpdate(e);
47518         
47519         if (this.throttle) {
47520             this.throttleStroke(this.strokeUpdate, this.throttle);
47521         }
47522         else {
47523             this.strokeUpdate(e);
47524         }
47525     },
47526     
47527     strokeBegin: function(e)
47528     {
47529         var newPointGroup = {
47530             color: this.dot_color,
47531             points: []
47532         };
47533         
47534         if (typeof this.onBegin === 'function') {
47535             this.onBegin(e);
47536         }
47537         
47538         this.curve_data.push(newPointGroup);
47539         this.reset();
47540         this.strokeUpdate(e);
47541     },
47542     
47543     strokeUpdate: function(e)
47544     {
47545         var rect = this.canvasEl().dom.getBoundingClientRect();
47546         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
47547         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
47548         var lastPoints = lastPointGroup.points;
47549         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
47550         var isLastPointTooClose = lastPoint
47551             ? point.distanceTo(lastPoint) <= this.min_distance
47552             : false;
47553         var color = lastPointGroup.color;
47554         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
47555             var curve = this.addPoint(point);
47556             if (!lastPoint) {
47557                 this.drawDot({color: color, point: point});
47558             }
47559             else if (curve) {
47560                 this.drawCurve({color: color, curve: curve});
47561             }
47562             lastPoints.push({
47563                 time: point.time,
47564                 x: point.x,
47565                 y: point.y
47566             });
47567         }
47568     },
47569     
47570     strokeEnd: function(e)
47571     {
47572         this.strokeUpdate(e);
47573         if (typeof this.onEnd === 'function') {
47574             this.onEnd(e);
47575         }
47576     },
47577     
47578     addPoint:  function (point) {
47579         var _lastPoints = this._lastPoints;
47580         _lastPoints.push(point);
47581         if (_lastPoints.length > 2) {
47582             if (_lastPoints.length === 3) {
47583                 _lastPoints.unshift(_lastPoints[0]);
47584             }
47585             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
47586             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
47587             _lastPoints.shift();
47588             return curve;
47589         }
47590         return null;
47591     },
47592     
47593     calculateCurveWidths: function (startPoint, endPoint) {
47594         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
47595             (1 - this.velocity_filter_weight) * this._lastVelocity;
47596
47597         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
47598         var widths = {
47599             end: newWidth,
47600             start: this._lastWidth
47601         };
47602         
47603         this._lastVelocity = velocity;
47604         this._lastWidth = newWidth;
47605         return widths;
47606     },
47607     
47608     drawDot: function (_a) {
47609         var color = _a.color, point = _a.point;
47610         var ctx = this.canvasElCtx();
47611         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
47612         ctx.beginPath();
47613         this.drawCurveSegment(point.x, point.y, width);
47614         ctx.closePath();
47615         ctx.fillStyle = color;
47616         ctx.fill();
47617     },
47618     
47619     drawCurve: function (_a) {
47620         var color = _a.color, curve = _a.curve;
47621         var ctx = this.canvasElCtx();
47622         var widthDelta = curve.endWidth - curve.startWidth;
47623         var drawSteps = Math.floor(curve.length()) * 2;
47624         ctx.beginPath();
47625         ctx.fillStyle = color;
47626         for (var i = 0; i < drawSteps; i += 1) {
47627         var t = i / drawSteps;
47628         var tt = t * t;
47629         var ttt = tt * t;
47630         var u = 1 - t;
47631         var uu = u * u;
47632         var uuu = uu * u;
47633         var x = uuu * curve.startPoint.x;
47634         x += 3 * uu * t * curve.control1.x;
47635         x += 3 * u * tt * curve.control2.x;
47636         x += ttt * curve.endPoint.x;
47637         var y = uuu * curve.startPoint.y;
47638         y += 3 * uu * t * curve.control1.y;
47639         y += 3 * u * tt * curve.control2.y;
47640         y += ttt * curve.endPoint.y;
47641         var width = curve.startWidth + ttt * widthDelta;
47642         this.drawCurveSegment(x, y, width);
47643         }
47644         ctx.closePath();
47645         ctx.fill();
47646     },
47647     
47648     drawCurveSegment: function (x, y, width) {
47649         var ctx = this.canvasElCtx();
47650         ctx.moveTo(x, y);
47651         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
47652         this.is_empty = false;
47653     },
47654     
47655     clear: function()
47656     {
47657         var ctx = this.canvasElCtx();
47658         var canvas = this.canvasEl().dom;
47659         ctx.fillStyle = this.bg_color;
47660         ctx.clearRect(0, 0, canvas.width, canvas.height);
47661         ctx.fillRect(0, 0, canvas.width, canvas.height);
47662         this.curve_data = [];
47663         this.reset();
47664         this.is_empty = true;
47665     },
47666     
47667     fileEl: function()
47668     {
47669         return  this.el.select('input',true).first();
47670     },
47671     
47672     canvasEl: function()
47673     {
47674         return this.el.select('canvas',true).first();
47675     },
47676     
47677     canvasElCtx: function()
47678     {
47679         return this.el.select('canvas',true).first().dom.getContext('2d');
47680     },
47681     
47682     getImage: function(type)
47683     {
47684         if(this.is_empty) {
47685             return false;
47686         }
47687         
47688         // encryption ?
47689         return this.canvasEl().dom.toDataURL('image/'+type, 1);
47690     },
47691     
47692     drawFromImage: function(img_src)
47693     {
47694         var img = new Image();
47695         
47696         img.onload = function(){
47697             this.canvasElCtx().drawImage(img, 0, 0);
47698         }.bind(this);
47699         
47700         img.src = img_src;
47701         
47702         this.is_empty = false;
47703     },
47704     
47705     selectImage: function()
47706     {
47707         this.fileEl().dom.click();
47708     },
47709     
47710     uploadImage: function(e)
47711     {
47712         var reader = new FileReader();
47713         
47714         reader.onload = function(e){
47715             var img = new Image();
47716             img.onload = function(){
47717                 this.reset();
47718                 this.canvasElCtx().drawImage(img, 0, 0);
47719             }.bind(this);
47720             img.src = e.target.result;
47721         }.bind(this);
47722         
47723         reader.readAsDataURL(e.target.files[0]);
47724     },
47725     
47726     // Bezier Point Constructor
47727     Point: (function () {
47728         function Point(x, y, time) {
47729             this.x = x;
47730             this.y = y;
47731             this.time = time || Date.now();
47732         }
47733         Point.prototype.distanceTo = function (start) {
47734             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
47735         };
47736         Point.prototype.equals = function (other) {
47737             return this.x === other.x && this.y === other.y && this.time === other.time;
47738         };
47739         Point.prototype.velocityFrom = function (start) {
47740             return this.time !== start.time
47741             ? this.distanceTo(start) / (this.time - start.time)
47742             : 0;
47743         };
47744         return Point;
47745     }()),
47746     
47747     
47748     // Bezier Constructor
47749     Bezier: (function () {
47750         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
47751             this.startPoint = startPoint;
47752             this.control2 = control2;
47753             this.control1 = control1;
47754             this.endPoint = endPoint;
47755             this.startWidth = startWidth;
47756             this.endWidth = endWidth;
47757         }
47758         Bezier.fromPoints = function (points, widths, scope) {
47759             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
47760             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
47761             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
47762         };
47763         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
47764             var dx1 = s1.x - s2.x;
47765             var dy1 = s1.y - s2.y;
47766             var dx2 = s2.x - s3.x;
47767             var dy2 = s2.y - s3.y;
47768             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
47769             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
47770             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
47771             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
47772             var dxm = m1.x - m2.x;
47773             var dym = m1.y - m2.y;
47774             var k = l2 / (l1 + l2);
47775             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
47776             var tx = s2.x - cm.x;
47777             var ty = s2.y - cm.y;
47778             return {
47779                 c1: new scope.Point(m1.x + tx, m1.y + ty),
47780                 c2: new scope.Point(m2.x + tx, m2.y + ty)
47781             };
47782         };
47783         Bezier.prototype.length = function () {
47784             var steps = 10;
47785             var length = 0;
47786             var px;
47787             var py;
47788             for (var i = 0; i <= steps; i += 1) {
47789                 var t = i / steps;
47790                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
47791                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
47792                 if (i > 0) {
47793                     var xdiff = cx - px;
47794                     var ydiff = cy - py;
47795                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
47796                 }
47797                 px = cx;
47798                 py = cy;
47799             }
47800             return length;
47801         };
47802         Bezier.prototype.point = function (t, start, c1, c2, end) {
47803             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
47804             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
47805             + (3.0 * c2 * (1.0 - t) * t * t)
47806             + (end * t * t * t);
47807         };
47808         return Bezier;
47809     }()),
47810     
47811     throttleStroke: function(fn, wait) {
47812       if (wait === void 0) { wait = 250; }
47813       var previous = 0;
47814       var timeout = null;
47815       var result;
47816       var storedContext;
47817       var storedArgs;
47818       var later = function () {
47819           previous = Date.now();
47820           timeout = null;
47821           result = fn.apply(storedContext, storedArgs);
47822           if (!timeout) {
47823               storedContext = null;
47824               storedArgs = [];
47825           }
47826       };
47827       return function wrapper() {
47828           var args = [];
47829           for (var _i = 0; _i < arguments.length; _i++) {
47830               args[_i] = arguments[_i];
47831           }
47832           var now = Date.now();
47833           var remaining = wait - (now - previous);
47834           storedContext = this;
47835           storedArgs = args;
47836           if (remaining <= 0 || remaining > wait) {
47837               if (timeout) {
47838                   clearTimeout(timeout);
47839                   timeout = null;
47840               }
47841               previous = now;
47842               result = fn.apply(storedContext, storedArgs);
47843               if (!timeout) {
47844                   storedContext = null;
47845                   storedArgs = [];
47846               }
47847           }
47848           else if (!timeout) {
47849               timeout = window.setTimeout(later, remaining);
47850           }
47851           return result;
47852       };
47853   }
47854   
47855 });
47856
47857  
47858
47859  // old names for form elements
47860 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
47861 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
47862 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
47863 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
47864 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
47865 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
47866 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
47867 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
47868 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
47869 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
47870 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
47871 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
47872 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
47873 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
47874 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
47875 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
47876 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
47877 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
47878 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
47879 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
47880 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
47881 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
47882 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
47883 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
47884 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
47885 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
47886
47887 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
47888 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
47889
47890 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
47891 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
47892
47893 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
47894 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
47895 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
47896 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
47897