Import src directory from OpenRico 2.1
[misc/kostenrechnung] / lib / rico / ricoStyles.js
1 /*
2  *  Copyright 2005 Sabre Airline Solutions
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
5  *  file except in compliance with the License. You may obtain a copy of the License at
6  *
7  *         http://www.apache.org/licenses/LICENSE-2.0
8  *
9  *  Unless required by applicable law or agreed to in writing, software distributed under the
10  *  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11  *  either express or implied. See the License for the specific language governing permissions
12  *  and limitations under the License.
13  */
14
15
16 Rico.Color = Class.create(
17 /** @lends Rico.Color# */
18 {
19 /**
20  * @class Methods to manipulate color values.
21  * @constructs
22  * @param red integer (0-255)
23  * @param green integer (0-255)
24  * @param blue integer (0-255)
25  */
26    initialize: function(red, green, blue) {
27       this.rgb = { r: red, g : green, b : blue };
28    },
29
30    setRed: function(r) {
31       this.rgb.r = r;
32    },
33
34    setGreen: function(g) {
35       this.rgb.g = g;
36    },
37
38    setBlue: function(b) {
39       this.rgb.b = b;
40    },
41
42    setHue: function(h) {
43
44       // get an HSB model, and set the new hue...
45       var hsb = this.asHSB();
46       hsb.h = h;
47
48       // convert back to RGB...
49       this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
50    },
51
52    setSaturation: function(s) {
53       // get an HSB model, and set the new hue...
54       var hsb = this.asHSB();
55       hsb.s = s;
56
57       // convert back to RGB and set values...
58       this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
59    },
60
61    setBrightness: function(b) {
62       // get an HSB model, and set the new hue...
63       var hsb = this.asHSB();
64       hsb.b = b;
65
66       // convert back to RGB and set values...
67       this.rgb = Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b );
68    },
69
70    darken: function(percent) {
71       var hsb  = this.asHSB();
72       this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0));
73    },
74
75    brighten: function(percent) {
76       var hsb  = this.asHSB();
77       this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1));
78    },
79
80    blend: function(other) {
81       this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2);
82       this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2);
83       this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2);
84    },
85
86    isBright: function() {
87       var hsb = this.asHSB();
88       return this.asHSB().b > 0.5;
89    },
90
91    isDark: function() {
92       return ! this.isBright();
93    },
94
95    asRGB: function() {
96       return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")";
97    },
98
99    asHex: function() {
100       return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart();
101    },
102
103    asHSB: function() {
104       return Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b);
105    },
106
107    toString: function() {
108       return this.asHex();
109    }
110
111 });
112
113 /**
114  * Factory method for creating a color from an RGB string
115  * @param hexCode a 3 or 6 digit hex string, optionally preceded by a # symbol
116  * @returns a Rico.Color object
117  */
118 Rico.Color.createFromHex = function(hexCode) {
119   if(hexCode.length==4) {
120     var shortHexCode = hexCode;
121     hexCode = '#';
122     for(var i=1;i<4;i++)
123       hexCode += (shortHexCode.charAt(i) + shortHexCode.charAt(i));
124   }
125   if ( hexCode.indexOf('#') == 0 )
126     hexCode = hexCode.substring(1);
127   if (!hexCode.match(/^[0-9A-Fa-f]{6}$/)) return null;
128   var red   = hexCode.substring(0,2);
129   var green = hexCode.substring(2,4);
130   var blue  = hexCode.substring(4,6);
131   return new Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) );
132 };
133
134 /**
135  * Retrieves the background color of an HTML element
136  * @param elem the DOM element whose background color should be retreived
137  * @returns a Rico.Color object
138  */
139 Rico.Color.createColorFromBackground = function(elem) {
140
141    if (!elem.style) return new Rico.Color(255,255,255);
142    var actualColor = Element.getStyle(elem, "background-color");
143
144    // if color is tranparent, check parent
145    // Safari returns "rgba(0, 0, 0, 0)", which means transparent
146    if ( actualColor.match(/^(transparent|rgba\(0,\s*0,\s*0,\s*0\))$/i) && elem.parentNode )
147       return Rico.Color.createColorFromBackground(elem.parentNode);
148
149    if (actualColor == null) return new Rico.Color(255,255,255);
150
151    if ( actualColor.indexOf("rgb(") == 0 ) {
152       var colors = actualColor.substring(4, actualColor.length - 1 );
153       var colorArray = colors.split(",");
154       return new Rico.Color( parseInt( colorArray[0],10 ),
155                              parseInt( colorArray[1],10 ),
156                              parseInt( colorArray[2],10 )  );
157
158    }
159    else if ( actualColor.indexOf("#") == 0 ) {
160       return Rico.Color.createFromHex(actualColor);
161    }
162    else
163       return new Rico.Color(255,255,255);
164 };
165
166 /**
167  * Converts hue/saturation/brightness to RGB
168  * @returns a 3-element object: r=red, g=green, b=blue.
169  */
170 Rico.Color.HSBtoRGB = function(hue, saturation, brightness) {
171
172   var red   = 0;
173         var green = 0;
174         var blue  = 0;
175
176   if (saturation == 0) {
177      red = parseInt(brightness * 255.0 + 0.5,10);
178            green = red;
179            blue = red;
180         }
181         else {
182       var h = (hue - Math.floor(hue)) * 6.0;
183       var f = h - Math.floor(h);
184       var p = brightness * (1.0 - saturation);
185       var q = brightness * (1.0 - saturation * f);
186       var t = brightness * (1.0 - (saturation * (1.0 - f)));
187
188       switch (parseInt(h,10)) {
189          case 0:
190             red   = (brightness * 255.0 + 0.5);
191             green = (t * 255.0 + 0.5);
192             blue  = (p * 255.0 + 0.5);
193             break;
194          case 1:
195             red   = (q * 255.0 + 0.5);
196             green = (brightness * 255.0 + 0.5);
197             blue  = (p * 255.0 + 0.5);
198             break;
199          case 2:
200             red   = (p * 255.0 + 0.5);
201             green = (brightness * 255.0 + 0.5);
202             blue  = (t * 255.0 + 0.5);
203             break;
204          case 3:
205             red   = (p * 255.0 + 0.5);
206             green = (q * 255.0 + 0.5);
207             blue  = (brightness * 255.0 + 0.5);
208             break;
209          case 4:
210             red   = (t * 255.0 + 0.5);
211             green = (p * 255.0 + 0.5);
212             blue  = (brightness * 255.0 + 0.5);
213             break;
214           case 5:
215             red   = (brightness * 255.0 + 0.5);
216             green = (p * 255.0 + 0.5);
217             blue  = (q * 255.0 + 0.5);
218             break;
219             }
220         }
221
222    return { r : parseInt(red,10), g : parseInt(green,10) , b : parseInt(blue,10) };
223 };
224
225 /**
226  * Converts RGB value to hue/saturation/brightness
227  * @param r integer (0-255)
228  * @param g integer (0-255)
229  * @param b integer (0-255)
230  * @returns a 3-element object: h=hue, s=saturation, b=brightness.
231  * (unlike some HSB documentation which states hue should be a value 0-360, this routine returns hue values from 0 to 1.0)
232  */
233 Rico.Color.RGBtoHSB = function(r, g, b) {
234
235    var hue;
236    var saturation;
237    var brightness;
238
239    var cmax = (r > g) ? r : g;
240    if (b > cmax)
241       cmax = b;
242
243    var cmin = (r < g) ? r : g;
244    if (b < cmin)
245       cmin = b;
246
247    brightness = cmax / 255.0;
248    if (cmax != 0)
249       saturation = (cmax - cmin)/cmax;
250    else
251       saturation = 0;
252
253    if (saturation == 0)
254       hue = 0;
255    else {
256       var redc   = (cmax - r)/(cmax - cmin);
257         var greenc = (cmax - g)/(cmax - cmin);
258         var bluec  = (cmax - b)/(cmax - cmin);
259
260         if (r == cmax)
261            hue = bluec - greenc;
262         else if (g == cmax)
263            hue = 2.0 + redc - bluec;
264       else
265            hue = 4.0 + greenc - redc;
266
267         hue = hue / 6.0;
268         if (hue < 0)
269            hue = hue + 1.0;
270    }
271
272    return { h : hue, s : saturation, b : brightness };
273 };
274
275 /**
276  * Creates a vertical gradient inside an element
277  * @param e element where gradient will be created
278  * @param startColor starting color, either a Rico.Color object or 6-character RGB string
279  * @param endColor ending color, either a Rico.Color object or 6-character RGB string
280  */
281 Rico.Color.createGradientV = function(e,startColor,endColor) {
282   var c1=typeof(startColor)=='string' ? Rico.Color.createFromHex(startColor) : startColor;
283   var c2=typeof(endColor)=='string' ? Rico.Color.createFromHex(endColor) : endColor;
284   if (Prototype.Browser.IE) {
285     e.style.filter = "progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=\"" + c1.asHex() + "\",EndColorStr=\"" + c2.asHex() + "\")";
286   } else {
287     var colorArray = Rico.Color.createColorPath(c1,c2,Math.min(e.offsetHeight,50));
288     var remh=e.offsetHeight,l=colorArray.length;
289     var div=Rico.Color.createGradientContainer();
290     var tmpDOM = document.createDocumentFragment();
291     for(var g,h,p=0;p<colorArray.length;p++) {
292       h = Math.round(remh/l) || 1;
293       g = document.createElement("div");
294       g.setAttribute("style","height:" + h + "px;width:100%;background-color:" + colorArray[p].asRGB() + ";");
295       tmpDOM.appendChild(g);
296       l--;
297       remh-=h;
298     }
299     div.appendChild(tmpDOM);
300     e.appendChild(div);
301     tmpDOM = null;
302   }
303 };
304
305 /**
306  * Creates a horizontal gradient inside an element
307  * @param e element where gradient will be created
308  * @param startColor starting color, either a Rico.Color object or 6-character RGB string
309  * @param endColor ending color, either a Rico.Color object or 6-character RGB string
310  */
311 Rico.Color.createGradientH = function(e,startColor,endColor) {
312   var c1=typeof(startColor)=='string' ? Rico.Color.createFromHex(startColor) : startColor;
313   var c2=typeof(endColor)=='string' ? Rico.Color.createFromHex(endColor) : endColor;
314   if (Prototype.Browser.IE) {
315     e.style.filter = "progid:DXImageTransform.Microsoft.Gradient(GradientType=1,StartColorStr=\"" + c1.asHex() + "\",EndColorStr=\"" + c2.asHex() + "\")";
316   } else {
317     var colorArray = Rico.Color.createColorPath(c1,c2,Math.min(e.offsetWidth,50));
318     var x=0,remw=e.offsetWidth,l=colorArray.length;
319     var div=Rico.Color.createGradientContainer();
320     var tmpDOM = document.createDocumentFragment();
321     for(var p=0;p<colorArray.length;p++) {
322       var w=Math.round(remw/l) || 1;
323       var g = document.createElement("div");
324       g.setAttribute("style","position:absolute;top:0px;left:" + x + "px;height:100%;width:" + w + "px;background-color:" + colorArray[p].asRGB() + ";");
325       tmpDOM.appendChild(g);
326       x+=w;
327       l--;
328       remw-=w;
329     }
330     div.appendChild(tmpDOM);
331     e.appendChild(div);
332     tmpDOM = null;
333   }
334 };
335
336 /** creates containing element for gradient methods */
337 Rico.Color.createGradientContainer = function() {
338   var div=document.createElement('div');
339   div.style.height='100%';
340   div.style.width='100%';
341   div.style.position='absolute';
342   div.style.top='0px';
343   div.style.left='0px';
344   div.style.zIndex=-1;
345   return div;
346 };
347
348 /** calculates intermediate color values for gradient methods */
349 Rico.Color.createColorPath = function(color1,color2,slices) {
350   var colorPath = [];
351   var colorPercent = 1.0;
352   var delta=1.0/slices;
353   do {
354     colorPath[colorPath.length]=Rico.Color.setColorHue(color1,colorPercent,color2);
355     colorPercent-=delta;
356   } while(colorPercent>0);
357   return colorPath;
358 };
359
360 Rico.Color.setColorHue = function(originColor,opacityPercent,maskRGB) {
361   return new Rico.Color(
362     Math.round(originColor.rgb.r*opacityPercent + maskRGB.rgb.r*(1.0-opacityPercent)),
363     Math.round(originColor.rgb.g*opacityPercent + maskRGB.rgb.g*(1.0-opacityPercent)),
364     Math.round(originColor.rgb.b*opacityPercent + maskRGB.rgb.b*(1.0-opacityPercent))
365   );
366 };
367
368
369 /**
370  * @namespace
371  */
372 Rico.Corner = {
373
374    round: function(e, options) {
375       e = $(e);
376       this._setOptions(options);
377       var color = this.options.color == "fromElement" ? this._background(e) : this.options.color;
378       var bgColor = this.options.bgColor == "fromParent" ? this._background(e.parentNode) : this.options.bgColor;
379       if (Prototype.Browser.Gecko && this.options.useMoz && !this.options.border && Element.getStyle(e,'background-image')=='none')
380         this._roundCornersGecko(e, color);
381       else if (typeof(Element.getStyle(e,'-webkit-border-radius'))=='string' && !this.options.border)
382         this._roundCornersWebKit(e, color);
383       else
384         this._roundCornersImpl(e, color, bgColor);
385    },
386
387    _roundCornersImpl: function(e, color, bgColor) {
388       this.options.numSlices = this.options.compact ? 2 : 4;
389       this.borderColor = this._borderColor(color,bgColor);
390       if(this.options.border)
391          this._renderBorder(e,bgColor);
392       if(this._isTopRounded())
393          this._roundTopCorners(e,color,bgColor);
394       if(this._isBottomRounded())
395          this._roundBottomCorners(e,color,bgColor);
396    },
397
398    _roundCornersGecko: function(e, color) {
399       var radius=this.options.compact ? '4px' : '8px';
400       if (this._hasString(this.options.corners, "all"))
401         Element.setStyle(e, {MozBorderRadius:radius}, true);
402       else {
403         if (this._hasString(this.options.corners, "top", "tl")) Element.setStyle(e, {MozBorderRadiusTopleft:radius}, true);
404         if (this._hasString(this.options.corners, "top", "tr")) Element.setStyle(e, {MozBorderRadiusTopright:radius}, true);
405         if (this._hasString(this.options.corners, "bottom", "bl")) Element.setStyle(e, {MozBorderRadiusBottomleft:radius}, true);
406         if (this._hasString(this.options.corners, "bottom", "br")) Element.setStyle(e, {MozBorderRadiusBottomright:radius}, true);
407       }
408    },
409
410    _roundCornersWebKit: function(e, color) {
411       var radius=this.options.compact ? '4px' : '8px';
412       if (this._hasString(this.options.corners, "all"))
413         Element.setStyle(e, {WebkitBorderRadius:radius}, true);
414       else {
415         if (this._hasString(this.options.corners, "top", "tl")) Element.setStyle(e, {WebkitBorderTopLeftRadius:radius}, true);
416         if (this._hasString(this.options.corners, "top", "tr")) Element.setStyle(e, {WebkitBorderTopRightRadius:radius}, true);
417         if (this._hasString(this.options.corners, "bottom", "bl")) Element.setStyle(e, {WebkitBorderBottomLeftRadius:radius}, true);
418         if (this._hasString(this.options.corners, "bottom", "br")) Element.setStyle(e, {WebkitBorderBottomRightRadius:radius}, true);
419       }
420    },
421
422    _renderBorder: function(el,bgColor) {
423       var wrapper=RicoUtil.wrapChildren(el);
424       var borderValue = "1px solid " + this._borderColor(bgColor);
425       Element.setStyle(wrapper,{
426         borderLeft: borderValue,
427         borderRight: borderValue,
428         marginTop: '0px',
429         marginBottom: '0px',
430         padding: '1px',  // prevents margin collapse
431         height: '100%'
432       });
433    },
434
435    _roundTopCorners: function(el, color, bgColor) {
436       var corner = this._createCorner(bgColor);
437       for(var i=0 ; i < this.options.numSlices ; i++ )
438          corner.appendChild(this._createCornerSlice(color,bgColor,i,"top"));
439       el.style.paddingTop = '0px';
440       el.insertBefore(corner,el.firstChild);
441    },
442
443    _roundBottomCorners: function(el, color, bgColor) {
444       var corner = this._createCorner(bgColor);
445       for(var i=(this.options.numSlices-1) ; i >= 0 ; i-- )
446          corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom"));
447       el.style.paddingBottom = 0;
448       el.appendChild(corner);
449    },
450
451    _createCorner: function(bgColor) {
452       var corner = document.createElement("div");
453       corner.style.backgroundColor = (this._isTransparent() ? "transparent" : bgColor);
454       return corner;
455    },
456
457    _createCornerSlice: function(color,bgColor, n, position) {
458       var slice = document.createElement("span");
459
460       var inStyle = slice.style;
461       inStyle.backgroundColor = color;
462       inStyle.display  = "block";
463       inStyle.height   = "1px";
464       inStyle.overflow = "hidden";
465       inStyle.fontSize = "1px";
466
467       if ( this.options.border && n == 0 ) {
468          inStyle.borderTopStyle    = "solid";
469          inStyle.borderTopWidth    = "1px";
470          inStyle.borderLeftWidth   = "0px";
471          inStyle.borderRightWidth  = "0px";
472          inStyle.borderBottomWidth = "0px";
473          inStyle.height            = "0px"; // assumes css compliant box model
474          inStyle.borderColor       = this.borderColor;
475       }
476       else if(this.borderColor) {
477          inStyle.borderColor = this.borderColor;
478          inStyle.borderStyle = "solid";
479          inStyle.borderWidth = "0px 1px";
480       }
481
482       if ( !this.options.compact && (n == (this.options.numSlices-1)) )
483          inStyle.height = "2px";
484
485       this._setMargin(slice, n, position);
486       this._setBorder(slice, n, position);
487       return slice;
488    },
489
490    _setOptions: function(options) {
491       this.options = {
492          corners : "all",
493          color   : "fromElement",
494          bgColor : "fromParent",
495          blend   : true,
496          border  : false,
497          compact : false,
498          useMoz  : true  // use native Gecko corners
499       };
500       Object.extend(this.options, options || {});
501       if (this._isTransparent()) this.options.blend = false;
502    },
503
504    _whichSideTop: function() {
505       if ( this._hasString(this.options.corners, "all", "top") )
506          return "";
507
508       if ( this.options.corners.indexOf("tl") >= 0 && this.options.corners.indexOf("tr") >= 0 )
509          return "";
510
511       if (this.options.corners.indexOf("tl") >= 0)
512          return "left";
513       else if (this.options.corners.indexOf("tr") >= 0)
514           return "right";
515       return "";
516    },
517
518    _whichSideBottom: function() {
519       if ( this._hasString(this.options.corners, "all", "bottom") )
520          return "";
521
522       if ( this.options.corners.indexOf("bl")>=0 && this.options.corners.indexOf("br")>=0 )
523          return "";
524
525       if(this.options.corners.indexOf("bl") >=0)
526          return "left";
527       else if(this.options.corners.indexOf("br")>=0)
528          return "right";
529       return "";
530    },
531
532    _borderColor : function(color,bgColor) {
533       if (color == "transparent") return bgColor;
534       if (this.options.border) return this.options.border;
535       if (!this.options.blend) return '';
536       var cc1 = Rico.Color.createFromHex(bgColor);
537       var cc2 = Rico.Color.createFromHex(color);
538       if (cc1==null || cc2==null) {
539          this.options.blend=false;
540          return '';
541       }
542       cc1.blend(cc2);
543       return cc1;
544    },
545
546
547    _setMargin: function(el, n, corners) {
548       var marginSize = this._marginSize(n);
549       var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
550
551       if ( whichSide == "left" ) {
552          el.style.marginLeft = marginSize + "px"; el.style.marginRight = "0px";
553       }
554       else if ( whichSide == "right" ) {
555          el.style.marginRight = marginSize + "px"; el.style.marginLeft  = "0px";
556       }
557       else {
558          el.style.marginLeft = marginSize + "px"; el.style.marginRight = marginSize + "px";
559       }
560    },
561
562    _setBorder: function(el,n,corners) {
563       var borderSize = this._borderSize(n);
564       var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
565       if ( whichSide == "left" ) {
566          el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = "0px";
567       }
568       else if ( whichSide == "right" ) {
569          el.style.borderRightWidth = borderSize + "px"; el.style.borderLeftWidth  = "0px";
570       }
571       else {
572          el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
573       }
574       if (this.options.border) {
575         el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
576       }
577    },
578
579    _marginSize: function(n) {
580       if ( this._isTransparent() )
581          return 0;
582
583       var marginSizes          = [ 5, 3, 2, 1 ];
584       var blendedMarginSizes   = [ 3, 2, 1, 0 ];
585       var compactMarginSizes   = [ 2, 1 ];
586       var smBlendedMarginSizes = [ 1, 0 ];
587
588       if ( this.options.compact && this.options.blend )
589          return smBlendedMarginSizes[n];
590       else if ( this.options.compact )
591          return compactMarginSizes[n];
592       else if ( this.options.blend )
593          return blendedMarginSizes[n];
594       else
595          return marginSizes[n];
596    },
597
598    _borderSize: function(n) {
599       var transparentBorderSizes = [ 5, 3, 2, 1 ];
600       var blendedBorderSizes     = [ 2, 1, 1, 1 ];
601       var compactBorderSizes     = [ 1, 0 ];
602       var actualBorderSizes      = [ 0, 2, 0, 0 ];
603
604       if ( this.options.compact && (this.options.blend || this._isTransparent()) )
605          return 1;
606       else if ( this.options.compact )
607          return compactBorderSizes[n];
608       else if ( this.options.blend )
609          return blendedBorderSizes[n];
610       else if ( this.options.border )
611          return actualBorderSizes[n];
612       else if ( this._isTransparent() )
613          return transparentBorderSizes[n];
614       return 0;
615    },
616
617    _background: function(elem) {
618      try {
619        var actualColor = Element.getStyle(elem, "background-color");
620
621        // if color is tranparent, check parent
622        // Safari returns "rgba(0, 0, 0, 0)", which means transparent
623        if ( actualColor.match(/^(transparent|rgba\(0,\s*0,\s*0,\s*0\))$/i) && elem.parentNode )
624           return this._background(elem.parentNode);
625
626        return actualColor == null ? "#ffffff" : actualColor;
627      } catch(err) {
628        return "#ffffff";
629      }
630    },
631
632    _hasString: function(str) {
633      for(var i=1 ; i<arguments.length ; i++) {
634        if (str.indexOf(arguments[i]) >= 0) return true;
635      }
636      return false;
637    },
638
639    _isTransparent: function() { return this.options.color == "transparent"; },
640    _isTopRounded: function() { return this._hasString(this.options.corners, "all", "top", "tl", "tr"); },
641    _isBottomRounded: function() { return this._hasString(this.options.corners, "all", "bottom", "bl", "br"); },
642    _hasSingleTextChild: function(el) { return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3; }
643 };
644
645 Rico.includeLoaded('ricoStyles.js');