Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(86)

Side by Side Diff: pkg/csslib/lib/src/property.dart

Issue 23168002: move csslib into dart svn (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pkg/csslib/lib/src/options.dart ('k') | pkg/csslib/lib/src/token.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 /** Representations of CSS styles. */
6
7 part of csslib.parser;
8
9 // TODO(terry): Prune down this file we do need some of the code in this file
10 // for darker, lighter, how to represent a Font, etc but alot of
11 // the complexity can be removed.
12 // See https://github.com/dart-lang/csslib/issues/7
13
14 /**
15 * Base for all style properties (e.g., Color, Font, Border, Margin, etc.)
16 */
17 abstract class _StyleProperty {
18 /**
19 * Returns the expression part of a CSS declaration. Declaration is:
20 *
21 * property:expression;
22 *
23 * E.g., if property is color then expression could be rgba(255,255,0) the
24 * CSS declaration would be 'color:rgba(255,255,0);'.
25 *
26 * then _cssExpression would return 'rgba(255,255,0)'. See
27 * <http://www.w3.org/TR/CSS21/grammar.html>
28 */
29 String get cssExpression;
30 }
31
32
33 /**
34 * Base interface for Color, HSL and RGB.
35 */
36 abstract class ColorBase {
37 /**
38 * Canonical form for color #rrggbb with alpha blending (0.0 == full
39 * transparency and 1.0 == fully opaque). If _argb length is 6 it's an
40 * rrggbb otherwise it's aarrggbb.
41 */
42 String toHexArgbString();
43
44 /**
45 * Return argb as a value (int).
46 */
47 int get argbValue;
48 }
49
50
51 /**
52 * General purpse Color class. Represent a color as an ARGB value that can be
53 * converted to and from num, hex string, hsl, hsla, rgb, rgba and SVG pre-
54 * defined color constant.
55 */
56 class Color implements _StyleProperty, ColorBase {
57 // If _argb length is 6 it's an rrggbb otherwise it's aarrggbb.
58 final String _argb;
59
60 // TODO(terry): Look at reducing Rgba and Hsla classes as factories for
61 // converting from Color to an Rgba or Hsla for reading only.
62 // Usefulness of creating an Rgba or Hsla is limited.
63
64 /**
65 * Create a color with an integer representing the rgb value of red, green,
66 * and blue. The value 0xffffff is the color white #ffffff (CSS style).
67 * The [rgb] value of 0xffd700 would map to #ffd700 or the constant
68 * Color.gold, where ff is red intensity, d7 is green intensity, and 00 is
69 * blue intensity.
70 */
71 Color(int rgb, [num alpha]) :
72 this._argb = Color._rgbToArgbString(rgb, alpha);
73
74 /**
75 * RGB takes three values. The [red], [green], and [blue] parameters are
76 * the intensity of those components where '0' is the least and '256' is the
77 * greatest.
78 *
79 * If [alpha] is provided, it is the level of translucency which ranges from
80 * '0' (completely transparent) to '1.0' (completely opaque). It will
81 * internally be mapped to an int between '0' and '255' like the other color
82 * components.
83 */
84 Color.createRgba(int red, int green, int blue, [num alpha]) :
85 this._argb = Color.convertToHexString(Color._clamp(red, 0, 255),
86 Color._clamp(green, 0, 255),
87 Color._clamp(blue, 0, 255),
88 alpha != null ? Color._clamp(alpha, 0, 1) : alpha);
89
90 /**
91 * Creates a new color from a CSS color string. For more information, see
92 * <https://developer.mozilla.org/en/CSS/color>.
93 */
94 Color.css(String color) :
95 this._argb = Color._convertCssToArgb(color);
96
97 // TODO(jmesserly): I found the use of percents a bit suprising.
98 /**
99 * HSL takes three values. The [hueDegree] degree on the color wheel; '0' is
100 * the least and '100' is the greatest. The value '0' or '360' is red, '120'
101 * is green, '240' is blue. Numbers in between reflect different shades.
102 * The [saturationPercent] percentage; where'0' is the least and '100' is the
103 * greatest (100 represents full color). The [lightnessPercent] percentage;
104 * where'0' is the least and '100' is the greatest. The value 0 is dark or
105 * black, 100 is light or white and 50 is a medium lightness.
106 *
107 * If [alpha] is provided, it is the level of translucency which ranges from
108 * '0' (completely transparent foreground) to '1.0' (completely opaque
109 * foreground).
110 */
111 Color.createHsla(num hueDegree, num saturationPercent, num lightnessPercent,
112 [num alpha]) :
113 this._argb = new Hsla(Color._clamp(hueDegree, 0, 360) / 360,
114 Color._clamp(saturationPercent, 0, 100) / 100,
115 Color._clamp(lightnessPercent, 0, 100) / 100,
116 alpha != null ? Color._clamp(alpha, 0, 1) : alpha).toHexArgbString();
117
118 /**
119 * The hslaRaw takes three values. The [hue] degree on the color wheel; '0'
120 * is the least and '1' is the greatest. The value '0' or '1' is red, the
121 * ratio of 120/360 is green, and the ratio of 240/360 is blue. Numbers in
122 * between reflect different shades. The [saturation] is a percentage; '0'
123 * is the least and '1' is the greatest. The value of '1' is equivalent to
124 * 100% (full colour). The [lightness] is a percentage; '0' is the least and
125 * '1' is the greatest. The value of '0' is dark (black), the value of '1'
126 * is light (white), and the value of '.50' is a medium lightness.
127 *
128 * The fourth optional parameter is:
129 * [alpha] level of translucency range of values is 0..1, zero is a
130 * completely transparent foreground and 1 is a completely
131 * opaque foreground.
132 */
133 Color.hslaRaw(num hue, num saturation, num lightness, [num alpha]) :
134 this._argb = new Hsla(Color._clamp(hue, 0, 1),
135 Color._clamp(saturation, 0, 1),
136 Color._clamp(lightness, 0, 1),
137 alpha != null ? Color._clamp(alpha, 0, 1) : alpha).toHexArgbString();
138
139 /**
140 * Generate a real constant for pre-defined colors (no leading #).
141 */
142 const Color.hex(this._argb);
143
144 // TODO(jmesserly): this is needed by the example so leave it exposed for now.
145 String toString() => cssExpression;
146
147 // TODO(terry): Regardless of how color is set (rgb, num, css or hsl) we'll
148 // always return a rgb or rgba loses fidelity when debugging in
149 // CSS if user uses hsl and would like to edit as hsl, etc. If
150 // this is an issue we should keep the original value and not re-
151 // create the CSS from the normalized value.
152 String get cssExpression {
153 if (_argb.length == 6) {
154 return "#$_argb"; // RGB only, no alpha blending.
155 } else {
156 num alpha = Color.hexToInt(_argb.substring(0, 2));
157 String a = (alpha / 255).toStringAsPrecision(2);
158 int r = Color.hexToInt(_argb.substring(2, 4));
159 int g = Color.hexToInt(_argb.substring(4, 6));
160 int b = Color.hexToInt(_argb.substring(6, 8));
161 return "rgba($r,$g,$b,$a)";
162 }
163 }
164
165 Rgba get rgba {
166 int nextIndex = 0;
167 num a;
168 if (_argb.length == 8) {
169 // Get alpha blending value 0..255
170 int alpha = Color.hexToInt(_argb.substring(nextIndex, nextIndex + 2));
171 // Convert to value from 0..1
172 a = double.parse((alpha / 255).toStringAsPrecision(2));
173 nextIndex += 2;
174 }
175 int r = Color.hexToInt(_argb.substring(nextIndex, nextIndex + 2));
176 nextIndex += 2;
177 int g = Color.hexToInt(_argb.substring(nextIndex, nextIndex + 2));
178 nextIndex += 2;
179 int b = Color.hexToInt(_argb.substring(nextIndex, nextIndex + 2));
180 return new Rgba(r, g, b, a);
181 }
182
183 Hsla get hsla => new Hsla.fromRgba(rgba);
184
185 int get argbValue => Color.hexToInt(_argb);
186
187 bool operator ==(Object other) => Color.equal(this, other);
188
189 String toHexArgbString() => _argb;
190
191 Color darker(num amount) {
192 Rgba newRgba = Color._createNewTintShadeFromRgba(rgba, -amount);
193 return new Color.hex("${newRgba.toHexArgbString()}");
194 }
195
196 Color lighter(num amount) {
197 Rgba newRgba = Color._createNewTintShadeFromRgba(rgba, amount);
198 return new Color.hex("${newRgba.toHexArgbString()}");
199 }
200
201 static bool equal(ColorBase curr, Object other) {
202 if (other is Color) {
203 Color o = other;
204 return o.toHexArgbString() == curr.toHexArgbString();
205 } else if (other is Rgba) {
206 Rgba rgb = other;
207 return rgb.toHexArgbString() == curr.toHexArgbString();
208 } else if (other is Hsla) {
209 Hsla hsla = other;
210 return hsla.toHexArgbString() == curr.toHexArgbString();
211 } else {
212 return false;
213 }
214 }
215
216 int get hashCode => _argb.hashCode;
217
218 // Conversion routines:
219
220 static String _rgbToArgbString(int rgba, num alpha) {
221 int a;
222 // If alpha is defined then adjust from 0..1 to 0..255 value, if not set
223 // then a is left as undefined and passed to convertToHexString.
224 if (alpha != null) {
225 a = (Color._clamp(alpha, 0, 1) * 255).round();
226 }
227
228 int r = (rgba & 0xff0000) >> 0x10;
229 int g = (rgba & 0xff00) >> 8;
230 int b = rgba & 0xff;
231
232 return Color.convertToHexString(r, g, b, a);
233 }
234
235 static const int _rgbCss = 1;
236 static const int _rgbaCss = 2;
237 static const int _hslCss = 3;
238 static const int _hslaCss = 4;
239 /**
240 * Parse CSS expressions of the from #rgb, rgb(r,g,b), rgba(r,g,b,a),
241 * hsl(h,s,l), hsla(h,s,l,a) and SVG colors (e.g., darkSlateblue, etc.) and
242 * convert to argb.
243 */
244 static String _convertCssToArgb(String value) {
245 // TODO(terry): Better parser/regex for converting CSS properties.
246 String color = value.trim().replaceAll("\\s", "");
247 if (color[0] == '#') {
248 String v = color.substring(1);
249 Color.hexToInt(v); // Valid hexadecimal, throws if not.
250 return v;
251 } else if (color.length > 0 && color[color.length - 1] == ')') {
252 int type;
253 if (color.indexOf("rgb(") == 0 || color.indexOf("RGB(") == 0) {
254 color = color.substring(4);
255 type = _rgbCss;
256 } else if (color.indexOf("rgba(") == 0 || color.indexOf("RGBA(") == 0) {
257 type = _rgbaCss;
258 color = color.substring(5);
259 } else if (color.indexOf("hsl(") == 0 || color.indexOf("HSL(") == 0) {
260 type = _hslCss;
261 color = color.substring(4);
262 } else if (color.indexOf("hsla(") == 0 || color.indexOf("HSLA(") == 0) {
263 type = _hslaCss;
264 color = color.substring(5);
265 } else {
266 throw new UnsupportedError('CSS property not implemented');
267 }
268
269 color = color.substring(0, color.length - 1); // Strip close paren.
270
271 var args = <num>[];
272 List<String> params = color.split(",");
273 for (String param in params) {
274 args.add(double.parse(param));
275 }
276 switch (type) {
277 case _rgbCss:
278 return Color.convertToHexString(args[0], args[1], args[2]);
279 case _rgbaCss:
280 return Color.convertToHexString(args[0], args[1], args[2], args[3]);
281 case _hslCss:
282 return new Hsla(args[0], args[1], args[2]).toHexArgbString();
283 case _hslaCss:
284 return new Hsla(args[0], args[1], args[2],
285 args[3]).toHexArgbString();
286 default:
287 // Type not defined UnsupportedOperationException should have thrown.
288 assert(true);
289 break;
290 }
291 }
292 }
293
294 /**
295 * [hex] hexadecimal string to convert to scalar.
296 * returns hexadecimal number as an integer.
297 * throws BadNumberFormatException if [hex] isn't a valid hexadecimal number.
298 */
299 // TODO(terry): Should be part of Dart standard library see bug
300 // <http://code.google.com/p/dart/issues/detail?id=2624>
301 static int hexToInt(String hex) {
302 int val = 0;
303
304 int len = hex.length;
305 for (int i = 0; i < len; i++) {
306 int hexDigit = hex.codeUnitAt(i);
307 if (hexDigit >= 48 && hexDigit <= 57) {
308 val += (hexDigit - 48) * (1 << (4 * (len - 1 - i)));
309 } else if (hexDigit >= 65 && hexDigit <= 70) {
310 // A..F
311 val += (hexDigit - 55) * (1 << (4 * (len - 1 - i)));
312 } else if (hexDigit >= 97 && hexDigit <= 102) {
313 // a..f
314 val += (hexDigit - 87) * (1 << (4 * (len - 1 - i)));
315 } else {
316 throw throw new FormatException("Bad hexadecimal value");
317 }
318 }
319
320 return val;
321 }
322
323 static String convertToHexString(int r, int g, int b, [num a]) {
324 String rHex = Color._numAs2DigitHex(Color._clamp(r, 0, 255));
325 String gHex = Color._numAs2DigitHex(Color._clamp(g, 0, 255));
326 String bHex = Color._numAs2DigitHex(Color._clamp(b, 0, 255));
327 String aHex = (a != null) ?
328 Color._numAs2DigitHex((Color._clamp(a, 0, 1) * 255).round()) : "";
329
330 // TODO(terry) 15.toRadixString(16) return 'F' on Dartium not f as in JS.
331 // bug: <http://code.google.com/p/dart/issues/detail?id=2670>
332 return "$aHex$rHex$gHex$bHex".toLowerCase();
333 }
334
335 static String _numAs2DigitHex(num v) {
336 // TODO(terry): v.toInt().toRadixString instead of v.toRadixString
337 // Bug <http://code.google.com/p/dart/issues/detail?id=2671>.
338 String hex = v.toInt().toRadixString(16);
339 if (hex.length == 1) {
340 hex = "0${hex}";
341 }
342 return hex;
343 }
344
345 static num _clamp(num value, num min, num max) =>
346 math.max(math.min(max, value), min);
347
348 /**
349 * Change the tint (make color lighter) or shade (make color darker) of all
350 * parts of [rgba] (r, g and b). The [amount] is percentage darker between
351 * -1 to 0 for darker and 0 to 1 for lighter; '0' is no change. The [amount]
352 * will darken or lighten the rgb values; it will not change the alpha value.
353 * If [amount] is outside of the value -1 to +1 then [amount] is changed to
354 * either the min or max direction -1 or 1.
355 *
356 * Darker will approach the color #000000 (black) and lighter will approach
357 * the color #ffffff (white).
358 */
359 static Rgba _createNewTintShadeFromRgba(Rgba rgba, num amount) {
360 int r, g, b;
361 num tintShade = Color._clamp(amount, -1, 1);
362 if (amount < 0 && rgba.r == 255 && rgba.g == 255 && rgba.b == 255) {
363 // TODO(terry): See TODO in _changeTintShadeColor; eliminate this test
364 // by converting to HSL and adjust lightness although this
365 // is fastest lighter/darker algorithm.
366 // Darkening white special handling.
367 r = Color._clamp((255 + (255 * tintShade)).round().toInt(), 0, 255);
368 g = Color._clamp((255 + (255 * tintShade)).round().toInt(), 0, 255);
369 b = Color._clamp((255 + (255 * tintShade)).round().toInt(), 0, 255);
370 } else {
371 // All other colors then darkening white go here.
372 r = Color._changeTintShadeColor(rgba.r, tintShade).round().toInt();
373 g = Color._changeTintShadeColor(rgba.g, tintShade).round().toInt();
374 b = Color._changeTintShadeColor(rgba.b, tintShade).round().toInt();
375 }
376 return new Rgba(r, g, b, rgba.a);
377 }
378
379 // TODO(terry): This does an okay lighter/darker; better would be convert to
380 // HSL then change the lightness.
381 /**
382 * The parameter [v] is the color to change (r, g, or b) in the range '0' to
383 * '255'. The parameter [delta] is a number between '-1' and '1'. A value
384 * between '-1' and '0' is darker and a value between '0' and '1' is lighter
385 * ('0' imples no change).
386 */
387 static num _changeTintShadeColor(num v, num delta) =>
388 Color._clamp(((1 - delta) * v + (delta * 255)).round(), 0, 255);
389
390 // Predefined CSS colors see <http://www.w3.org/TR/css3-color/>
391 static final Color transparent = const Color.hex("00ffffff"); // Alpha 0.0
392 static final Color aliceBlue = const Color.hex("0f08ff");
393 static final Color antiqueWhite = const Color.hex("0faebd7");
394 static final Color aqua = const Color.hex("00ffff");
395 static final Color aquaMarine = const Color.hex("7fffd4");
396 static final Color azure = const Color.hex("f0ffff");
397 static final Color beige = const Color.hex("f5f5dc");
398 static final Color bisque = const Color.hex("ffe4c4");
399 static final Color black = const Color.hex("000000");
400 static final Color blanchedAlmond = const Color.hex("ffebcd");
401 static final Color blue = const Color.hex("0000ff");
402 static final Color blueViolet = const Color.hex("8a2be2");
403 static final Color brown = const Color.hex("a52a2a");
404 static final Color burlyWood = const Color.hex("deb887");
405 static final Color cadetBlue = const Color.hex("5f9ea0");
406 static final Color chartreuse = const Color.hex("7fff00");
407 static final Color chocolate = const Color.hex("d2691e");
408 static final Color coral = const Color.hex("ff7f50");
409 static final Color cornFlowerBlue = const Color.hex("6495ed");
410 static final Color cornSilk = const Color.hex("fff8dc");
411 static final Color crimson = const Color.hex("dc143c");
412 static final Color cyan = const Color.hex("00ffff");
413 static final Color darkBlue = const Color.hex("00008b");
414 static final Color darkCyan = const Color.hex("008b8b");
415 static final Color darkGoldenRod = const Color.hex("b8860b");
416 static final Color darkGray = const Color.hex("a9a9a9");
417 static final Color darkGreen = const Color.hex("006400");
418 static final Color darkGrey = const Color.hex("a9a9a9");
419 static final Color darkKhaki = const Color.hex("bdb76b");
420 static final Color darkMagenta = const Color.hex("8b008b");
421 static final Color darkOliveGreen = const Color.hex("556b2f");
422 static final Color darkOrange = const Color.hex("ff8c00");
423 static final Color darkOrchid = const Color.hex("9932cc");
424 static final Color darkRed = const Color.hex("8b0000");
425 static final Color darkSalmon = const Color.hex("e9967a");
426 static final Color darkSeaGreen = const Color.hex("8fbc8f");
427 static final Color darkSlateBlue = const Color.hex("483d8b");
428 static final Color darkSlateGray = const Color.hex("2f4f4f");
429 static final Color darkSlateGrey = const Color.hex("2f4f4f");
430 static final Color darkTurquoise = const Color.hex("00ced1");
431 static final Color darkViolet = const Color.hex("9400d3");
432 static final Color deepPink = const Color.hex("ff1493");
433 static final Color deepSkyBlue = const Color.hex("00bfff");
434 static final Color dimGray = const Color.hex("696969");
435 static final Color dimGrey = const Color.hex("696969");
436 static final Color dodgerBlue = const Color.hex("1e90ff");
437 static final Color fireBrick = const Color.hex("b22222");
438 static final Color floralWhite = const Color.hex("fffaf0");
439 static final Color forestGreen = const Color.hex("228b22");
440 static final Color fuchsia = const Color.hex("ff00ff");
441 static final Color gainsboro = const Color.hex("dcdcdc");
442 static final Color ghostWhite = const Color.hex("f8f8ff");
443 static final Color gold = const Color.hex("ffd700");
444 static final Color goldenRod = const Color.hex("daa520");
445 static final Color gray = const Color.hex("808080");
446 static final Color green = const Color.hex("008000");
447 static final Color greenYellow = const Color.hex("adff2f");
448 static final Color grey = const Color.hex("808080");
449 static final Color honeydew = const Color.hex("f0fff0");
450 static final Color hotPink = const Color.hex("ff69b4");
451 static final Color indianRed = const Color.hex("cd5c5c");
452 static final Color indigo = const Color.hex("4b0082");
453 static final Color ivory = const Color.hex("fffff0");
454 static final Color khaki = const Color.hex("f0e68c");
455 static final Color lavender = const Color.hex("e6e6fa");
456 static final Color lavenderBlush = const Color.hex("fff0f5");
457 static final Color lawnGreen = const Color.hex("7cfc00");
458 static final Color lemonChiffon = const Color.hex("fffacd");
459 static final Color lightBlue = const Color.hex("add8e6");
460 static final Color lightCoral = const Color.hex("f08080");
461 static final Color lightCyan = const Color.hex("e0ffff");
462 static final Color lightGoldenRodYellow = const Color.hex("fafad2");
463 static final Color lightGray = const Color.hex("d3d3d3");
464 static final Color lightGreen = const Color.hex("90ee90");
465 static final Color lightGrey = const Color.hex("d3d3d3");
466 static final Color lightPink = const Color.hex("ffb6c1");
467 static final Color lightSalmon = const Color.hex("ffa07a");
468 static final Color lightSeaGreen = const Color.hex("20b2aa");
469 static final Color lightSkyBlue = const Color.hex("87cefa");
470 static final Color lightSlateGray = const Color.hex("778899");
471 static final Color lightSlateGrey = const Color.hex("778899");
472 static final Color lightSteelBlue = const Color.hex("b0c4de");
473 static final Color lightYellow = const Color.hex("ffffe0");
474 static final Color lime = const Color.hex("00ff00");
475 static final Color limeGreen = const Color.hex("32cd32");
476 static final Color linen = const Color.hex("faf0e6");
477 static final Color magenta = const Color.hex("ff00ff");
478 static final Color maroon = const Color.hex("800000");
479 static final Color mediumAquaMarine = const Color.hex("66cdaa");
480 static final Color mediumBlue = const Color.hex("0000cd");
481 static final Color mediumOrchid = const Color.hex("ba55d3");
482 static final Color mediumPurple = const Color.hex("9370db");
483 static final Color mediumSeaGreen = const Color.hex("3cb371");
484 static final Color mediumSlateBlue = const Color.hex("7b68ee");
485 static final Color mediumSpringGreen = const Color.hex("00fa9a");
486 static final Color mediumTurquoise = const Color.hex("48d1cc");
487 static final Color mediumVioletRed = const Color.hex("c71585");
488 static final Color midnightBlue = const Color.hex("191970");
489 static final Color mintCream = const Color.hex("f5fffa");
490 static final Color mistyRose = const Color.hex("ffe4e1");
491 static final Color moccasin = const Color.hex("ffe4b5");
492 static final Color navajoWhite = const Color.hex("ffdead");
493 static final Color navy = const Color.hex("000080");
494 static final Color oldLace = const Color.hex("fdf5e6");
495 static final Color olive = const Color.hex("808000");
496 static final Color oliveDrab = const Color.hex("6b8e23");
497 static final Color orange = const Color.hex("ffa500");
498 static final Color orangeRed = const Color.hex("ff4500");
499 static final Color orchid = const Color.hex("da70d6");
500 static final Color paleGoldenRod = const Color.hex("eee8aa");
501 static final Color paleGreen = const Color.hex("98fb98");
502 static final Color paleTurquoise = const Color.hex("afeeee");
503 static final Color paleVioletRed = const Color.hex("db7093");
504 static final Color papayaWhip = const Color.hex("ffefd5");
505 static final Color peachPuff = const Color.hex("ffdab9");
506 static final Color peru = const Color.hex("cd85ef");
507 static final Color pink = const Color.hex("ffc0cb");
508 static final Color plum = const Color.hex("dda0dd");
509 static final Color powderBlue = const Color.hex("b0e0e6");
510 static final Color purple = const Color.hex("800080");
511 static final Color red = const Color.hex("ff0000");
512 static final Color rosyBrown = const Color.hex("bc8f8f");
513 static final Color royalBlue = const Color.hex("4169e1");
514 static final Color saddleBrown = const Color.hex("8b4513");
515 static final Color salmon = const Color.hex("fa8072");
516 static final Color sandyBrown = const Color.hex("f4a460");
517 static final Color seaGreen = const Color.hex("2e8b57");
518 static final Color seashell = const Color.hex("fff5ee");
519 static final Color sienna = const Color.hex("a0522d");
520 static final Color silver = const Color.hex("c0c0c0");
521 static final Color skyBlue = const Color.hex("87ceeb");
522 static final Color slateBlue = const Color.hex("6a5acd");
523 static final Color slateGray = const Color.hex("708090");
524 static final Color slateGrey = const Color.hex("708090");
525 static final Color snow = const Color.hex("fffafa");
526 static final Color springGreen = const Color.hex("00ff7f");
527 static final Color steelBlue = const Color.hex("4682b4");
528 static final Color tan = const Color.hex("d2b48c");
529 static final Color teal = const Color.hex("008080");
530 static final Color thistle = const Color.hex("d8bfd8");
531 static final Color tomato = const Color.hex("ff6347");
532 static final Color turquoise = const Color.hex("40e0d0");
533 static final Color violet = const Color.hex("ee82ee");
534 static final Color wheat = const Color.hex("f5deb3");
535 static final Color white = const Color.hex("ffffff");
536 static final Color whiteSmoke = const Color.hex("f5f5f5");
537 static final Color yellow = const Color.hex("ffff00");
538 static final Color yellowGreen = const Color.hex("9acd32");
539 }
540
541
542 /**
543 * Rgba class for users that want to interact with a color as a RGBA value.
544 */
545 class Rgba implements _StyleProperty, ColorBase {
546 // TODO(terry): Consider consolidating rgba to a single 32-bit int, make sure
547 // it works under JS and Dart VM.
548 final int r;
549 final int g;
550 final int b;
551 final num a;
552
553 Rgba(int red, int green, int blue, [num alpha]) :
554 this.r = Color._clamp(red, 0, 255),
555 this.g = Color._clamp(green, 0, 255),
556 this.b = Color._clamp(blue, 0, 255),
557 this.a = (alpha != null) ? Color._clamp(alpha, 0, 1) : alpha;
558
559 factory Rgba.fromString(String hexValue) =>
560 new Color.css("#${Color._convertCssToArgb(hexValue)}").rgba;
561
562 factory Rgba.fromColor(Color color) => color.rgba;
563
564 factory Rgba.fromArgbValue(num value) {
565 return new Rgba(((value.toInt() & 0xff000000) >> 0x18), /* a */
566 ((value.toInt() & 0xff0000) >> 0x10), /* r */
567 ((value.toInt() & 0xff00) >> 8), /* g */
568 ((value.toInt() & 0xff))); /* b */
569 }
570
571 factory Rgba.fromHsla(Hsla hsla) {
572 // Convert to Rgba.
573 // See site <http://easyrgb.com/index.php?X=MATH> for good documentation
574 // and color conversion routines.
575
576 num h = hsla.hue;
577 num s = hsla.saturation;
578 num l = hsla.lightness;
579 num a = hsla.alpha;
580
581 int r;
582 int g;
583 int b;
584
585 if (s == 0) {
586 r = (l * 255).round().toInt();
587 g = r;
588 b = r;
589 } else {
590 num var2;
591
592 if (l < 0.5) {
593 var2 = l * (1 + s);
594 } else {
595 var2 = (l + s) - (s * l);
596 }
597 num var1 = 2 * l - var2;
598
599 r = (255 * Rgba._hueToRGB(var1, var2, h + (1/3))).round().toInt();
600 g = (255 * Rgba._hueToRGB(var1, var2, h)).round().toInt();
601 b = (255 * Rgba._hueToRGB(var1, var2, h - (1/3))).round().toInt();
602 }
603
604 return new Rgba(r, g, b, a);
605 }
606
607 static num _hueToRGB(num v1, num v2, num vH) {
608 if (vH < 0) {
609 vH += 1;
610 }
611
612 if (vH > 1) {
613 vH -= 1;
614 }
615
616 if ((6 * vH) < 1) {
617 return (v1 + (v2 - v1) * 6 * vH);
618 }
619
620 if ((2 * vH) < 1) {
621 return v2;
622 }
623
624 if ((3 * vH) < 2) {
625 return (v1 + (v2 - v1) * ((2 / 3 - vH) * 6));
626 }
627
628 return v1;
629 }
630
631 bool operator ==(Object other) => Color.equal(this, other);
632
633 String get cssExpression {
634 if (a == null) {
635 return "#${Color.convertToHexString(r, g, b)}";
636 } else {
637 return "rgba($r,$g,$b,$a)";
638 }
639 }
640
641 String toHexArgbString() => Color.convertToHexString(r, g, b, a);
642
643 int get argbValue {
644 int value = 0;
645 if (a != null) {
646 value = (a.toInt() << 0x18);
647 }
648 value += (r << 0x10);
649 value += (g << 0x08);
650 value += b;
651 }
652
653 Color get color => new Color.createRgba(r, g, b, a);
654 Hsla get hsla => new Hsla.fromRgba(this);
655
656 Rgba darker(num amount) => Color._createNewTintShadeFromRgba(this, -amount);
657 Rgba lighter(num amount) => Color._createNewTintShadeFromRgba(this, amount);
658
659 int get hashCode => toHexArgbString().hashCode;
660 }
661
662
663 /**
664 * Hsl class support to interact with a color as a hsl with hue, saturation, and
665 * lightness with optional alpha blending. The hue is a ratio of 360 degrees
666 * 360° = 1 or 0, (1° == (1/360)), saturation and lightness is a 0..1 fraction
667 * (1 == 100%) and alpha is a 0..1 fraction.
668 */
669 class Hsla implements _StyleProperty, ColorBase {
670 final num _h; // Value from 0..1
671 final num _s; // Value from 0..1
672 final num _l; // Value from 0..1
673 final num _a; // Value from 0..1
674
675 /**
676 * [hue] is a 0..1 fraction of 360 degrees (360 == 0).
677 * [saturation] is a 0..1 fraction (100% == 1).
678 * [lightness] is a 0..1 fraction (100% == 1).
679 * [alpha] is a 0..1 fraction, alpha blending between 0..1, 1 == 100% opaque.
680 */
681 Hsla(num hue, num saturation, num lightness, [num alpha]) :
682 this._h = (hue == 1) ? 0 : Color._clamp(hue, 0, 1),
683 this._s = Color._clamp(saturation, 0, 1),
684 this._l = Color._clamp(lightness, 0, 1),
685 this._a = (alpha != null) ? Color._clamp(alpha, 0, 1) : alpha;
686
687 factory Hsla.fromString(String hexValue) {
688 Rgba rgba = new Color.css("#${Color._convertCssToArgb(hexValue)}").rgba;
689 return _createFromRgba(rgba.r, rgba.g, rgba.b, rgba.a);
690 }
691
692 factory Hsla.fromColor(Color color) {
693 Rgba rgba = color.rgba;
694 return _createFromRgba(rgba.r, rgba.g, rgba.b, rgba.a);
695 }
696
697 factory Hsla.fromArgbValue(num value) {
698 num a = (value.toInt() & 0xff000000) >> 0x18;
699 int r = (value.toInt() & 0xff0000) >> 0x10;
700 int g = (value.toInt() & 0xff00) >> 8;
701 int b = value.toInt() & 0xff;
702
703 // Convert alpha to 0..1 from (0..255).
704 if (a != null) {
705 a = double.parse((a / 255).toStringAsPrecision(2));
706 }
707
708 return _createFromRgba(r, g, b, a);
709 }
710
711 factory Hsla.fromRgba(Rgba rgba) =>
712 _createFromRgba(rgba.r, rgba.g, rgba.b, rgba.a);
713
714 static Hsla _createFromRgba(num r, num g, num b, num a) {
715 // Convert RGB to hsl.
716 // See site <http://easyrgb.com/index.php?X=MATH> for good documentation
717 // and color conversion routines.
718 r /= 255;
719 g /= 255;
720 b /= 255;
721
722 // Hue, saturation and lightness.
723 num h;
724 num s;
725 num l;
726
727 num minRgb = math.min(r, math.min(g, b));
728 num maxRgb = math.max(r, math.max(g, b));
729 l = (maxRgb + minRgb) / 2;
730 if (l <= 0) {
731 return new Hsla(0, 0, l); // Black;
732 }
733
734 num vm = maxRgb - minRgb;
735 s = vm;
736 if (s > 0) {
737 s /= (l < 0.5) ? (maxRgb + minRgb) : (2 - maxRgb - minRgb);
738 } else {
739 return new Hsla(0, 0, l); // White
740 }
741
742 num r2, g2, b2;
743 r2 = (maxRgb - r) / vm;
744 g2 = (maxRgb - g) / vm;
745 b2 = (maxRgb - b) / vm;
746 if (r == maxRgb) {
747 h = (g == minRgb) ? 5.0 + b2 : 1 - g2;
748 } else if (g == maxRgb) {
749 h = (b == minRgb) ? 1 + r2 : 3 - b2;
750 } else {
751 h = (r == minRgb) ? 3 + g2 : 5 - r2;
752 }
753 h /= 6;
754
755 return new Hsla(h, s, l, a);
756 }
757
758 /**
759 * Returns 0..1 fraction (ratio of 360°, e.g. 1° == 1/360).
760 */
761 num get hue => _h;
762
763 /**
764 * Returns 0..1 fraction (1 == 100%)
765 */
766 num get saturation => _s;
767
768 /**
769 * Returns 0..1 fraction (1 == 100%).
770 */
771 num get lightness => _l;
772
773 /**
774 * Returns number as degrees 0..360.
775 */
776 num get hueDegrees => (_h * 360).round();
777
778 /**
779 * Returns number as percentage 0..100
780 */
781 num get saturationPercentage => (_s * 100).round();
782
783 /**
784 * Returns number as percentage 0..100.
785 */
786 num get lightnessPercentage => (_l * 100).round();
787
788 /**
789 * Returns number as 0..1
790 */
791 num get alpha => _a;
792
793 bool operator ==(Object other) => Color.equal(this, other);
794
795 String get cssExpression => (_a == null) ?
796 "hsl($hueDegrees,$saturationPercentage,$lightnessPercentage)" :
797 "hsla($hueDegrees,$saturationPercentage,$lightnessPercentage,$_a)";
798
799 String toHexArgbString() => new Rgba.fromHsla(this).toHexArgbString();
800
801 int get argbValue => Color.hexToInt(this.toHexArgbString());
802
803 Color get color => new Color.createHsla(_h, _s, _l, _a);
804 Rgba get rgba => new Rgba.fromHsla(this);
805
806 Hsla darker(num amount) =>
807 new Hsla.fromRgba(new Rgba.fromHsla(this).darker(amount));
808
809 Hsla lighter(num amount) =>
810 new Hsla.fromRgba(new Rgba.fromHsla(this).lighter(amount));
811
812 int get hashCode => toHexArgbString().hashCode;
813 }
814
815
816 /** X,Y position. */
817 class PointXY implements _StyleProperty {
818 final num x, y;
819 const PointXY(this.x, this.y);
820
821 String get cssExpression {
822 // TODO(terry): TBD
823 }
824 }
825
826
827 // TODO(terry): Implement style and color.
828 /**
829 * Supports border for measuring with layout.
830 */
831 class Border implements _StyleProperty {
832 final int top, left, bottom, right;
833
834 // TODO(terry): Just like CSS, 1-arg -> set all properties, 2-args -> top and
835 // bottom are first arg, left and right are second, 3-args, and
836 // 4-args -> tlbr or trbl.
837 const Border([this.top, this.left, this.bottom, this.right]);
838
839 // TODO(terry): Consider using Size or width and height.
840 Border.uniform(num amount) :
841 top = amount, left = amount, bottom = amount, right = amount;
842
843 int get width => left + right;
844 int get height => top + bottom;
845
846 String get cssExpression {
847 return (top == left && bottom == right && top == right) ? "${left}px" :
848 "${top != null ? '$top' : '0'}px ${
849 right != null ? '$right' : '0'}px ${
850 bottom != null ? '$bottom' : '0'}px ${
851 left != null ? '$left' : '0'}px";
852 }
853 }
854
855
856 /** Font style constants. */
857 class FontStyle {
858 /** Font style [normal] default. */
859 static const String normal = "normal";
860 /**
861 * Font style [italic] use explicity crafted italic font otherwise inclined
862 * on the fly like oblique.
863 */
864 static const String italic = "italic";
865 /**
866 * Font style [oblique] is rarely used. The normal style of a font is inclined
867 * on the fly to the right by 8-12 degrees.
868 */
869 static const String oblique = "oblique";
870 }
871
872
873 /** Font variant constants. */
874 class FontVariant {
875 /** Font style [normal] default. */
876 static const String normal = "normal";
877 /** Font variant [smallCaps]. */
878 static const String smallCaps = "small-caps";
879 }
880
881
882 /** Font weight constants values 100, 200, 300, 400, 500, 600, 700, 800, 900. */
883 class FontWeight {
884 /** Font weight normal [default] */
885 static const int normal = 400;
886 /** Font weight bold */
887 static const int bold = 700;
888
889 static const int wt100 = 100;
890 static const int wt200 = 200;
891 static const int wt300 = 300;
892 static const int wt400 = 400;
893 static const int wt500 = 500;
894 static const int wt600 = 600;
895 static const int wt700 = 700;
896 static const int wt800 = 800;
897 static const int wt900 = 900;
898 }
899
900
901 /** Generic font family names. */
902 class FontGeneric {
903 /** Generic family sans-serif font (w/o serifs). */
904 static const String sansSerif = "sans-serif";
905 /** Generic family serif font. */
906 static const String serif = "serif";
907 /** Generic family fixed-width font. */
908 static const monospace = "monospace";
909 /** Generic family emulate handwriting font. */
910 static const String cursive = "cursive";
911 /** Generic family decorative font. */
912 static const String fantasy = "fantasy";
913 }
914
915
916 /**
917 * List of most common font families across different platforms. Use the
918 * collection names in the Font class (e.g., Font.SANS_SERIF, Font.FONT_SERIF,
919 * Font.MONOSPACE, Font.CURSIVE or Font.FANTASY). These work best on all
920 * platforms using the fonts that best match availability on each platform.
921 * See <http://www.angelfire.com/al4/rcollins/style/fonts.html> for a good
922 * description of fonts available between platforms and browsers.
923 */
924 class FontFamily {
925 /** Sans-Serif font for Windows similar to Helvetica on Mac bold/italic. */
926 static const String arial = "arial";
927 /** Sans-Serif font for Windows less common already bolded. */
928 static const String arialBlack = "arial black";
929 /** Sans-Serif font for Mac since 1984, similar to Arial/Helvetica. */
930 static const String geneva = "geneva";
931 /** Sans-Serif font for Windows most readable sans-serif font for displays. */
932 static const String verdana = "verdana";
933 /** Sans-Serif font for Mac since 1984 is identical to Arial. */
934 static const String helvetica = "helvetica";
935
936 /** Serif font for Windows traditional font with “old-style” numerals. */
937 static const String georgia = "georgia";
938 /**
939 * Serif font for Mac. PCs may have the non-scalable Times use Times New
940 * Roman instead. Times is more compact than Times New Roman.
941 */
942 static const String times = "times";
943 /**
944 * Serif font for Windows most common serif font and default serif font for
945 * most browsers.
946 */
947 static const String timesNewRoman = "times new roman";
948
949 /**
950 * Monospace font for Mac/Windows most common. Scalable on Mac not scalable
951 * on Windows.
952 */
953 static const String courier = "courier";
954 /** Monospace font for Mac/Windows scalable on both platforms. */
955 static const String courierNew = "courier new";
956
957 /** Cursive font for Windows and default cursive font for IE. */
958 static const String comicSansMs = "comic sans ms";
959 /** Cursive font for Mac on Macs 2000 and newer. */
960 static const String textile = "textile";
961 /** Cursive font for older Macs. */
962 static const String appleChancery = "apple chancery";
963 /** Cursive font for some PCs. */
964 static const String zaphChancery = "zaph chancery";
965
966 /** Fantasy font on most Mac/Windows/Linux platforms. */
967 static const String impact = "impact";
968 /** Fantasy font for Windows. */
969 static const String webdings = "webdings";
970 }
971
972 class LineHeight {
973 final num height;
974 final bool inPixels;
975 const LineHeight(this.height, {this.inPixels : true});
976 }
977
978 // TODO(terry): Support @font-face fule.
979 /**
980 * Font style support for size, family, weight, style, variant, and lineheight.
981 */
982 class Font implements _StyleProperty {
983 /** Collection of most common sans-serif fonts in order. */
984 static const List<String> sansSerif = const [FontFamily.arial,
985 FontFamily.verdana,
986 FontFamily.geneva,
987 FontFamily.helvetica,
988 FontGeneric.sansSerif];
989
990 /** Collection of most common serif fonts in order. */
991 static const List<String> serif = const [FontFamily.georgia,
992 FontFamily.timesNewRoman,
993 FontFamily.times,
994 FontGeneric.serif];
995 /** Collection of most common monospace fonts in order. */
996 static const List<String> monospace = const [FontFamily.courierNew,
997 FontFamily.courier,
998 FontGeneric.monospace];
999 /** Collection of most common cursive fonts in order. */
1000 static const List<String> cursive = const [FontFamily.textile,
1001 FontFamily.appleChancery,
1002 FontFamily.zaphChancery,
1003 FontGeneric.fantasy];
1004 /** Collection of most common fantasy fonts in order. */
1005 static const List<String> fantasy = const [FontFamily.comicSansMs,
1006 FontFamily.impact,
1007 FontFamily.webdings,
1008 FontGeneric.fantasy];
1009
1010 // TODO(terry): Should support the values xx-small, small, large, xx-large,
1011 // etc. (mapped to a pixel sized font)?
1012 /** Font size in pixels. */
1013 final num size;
1014
1015 // TODO(terry): _family should be an immutable list, wrapper class to do this
1016 // should exist in Dart.
1017 /**
1018 * Family specifies a list of fonts, the browser will sequentially select the
1019 * the first known/supported font. There are two types of font families the
1020 * family-name (e.g., arial, times, courier, etc) or the generic-family (e.g.,
1021 * serif, sans-seric, etc.)
1022 */
1023 final List<String> family;
1024
1025 /** Font weight from 100, 200, 300, 400, 500, 600, 700, 800, 900 */
1026 final int weight;
1027
1028 /** Style of a font normal, italic, oblique. */
1029 final String style;
1030
1031 /**
1032 * Font variant NORMAL (default) or SMALL_CAPS. Different set of font glyph
1033 * lower case letters designed to have to fit within the font-height and
1034 * weight of the corresponding lowercase letters.
1035 */
1036 final String variant;
1037
1038 final LineHeight lineHeight;
1039
1040 // TODO(terry): Size and computedLineHeight are in pixels. Need to figure out
1041 // how to handle in other units (specified in other units) like
1042 // points, inches, etc. Do we have helpers like Units.Points(12)
1043 // where 12 is in points and that's converted to pixels?
1044 // TODO(terry): lineHeight is computed as 1.2 although CSS_RESET is 1.0 we
1045 // need to be consistent some browsers use 1 others 1.2.
1046 // TODO(terry): There is a school of thought "Golden Ratio Typography".
1047 // Where width to display the text is also important in computing the line
1048 // height. Classic typography suggest the ratio be 1.5. See
1049 // <http://www.pearsonified.com/2011/12/golden-ratio-typography.php> and
1050 // <http://meyerweb.com/eric/thoughts/2008/05/06/line-height-abnormal/>.
1051 /**
1052 * Create a font using [size] of font in pixels, [family] name of font(s)
1053 * using [FontFamily], [style] of the font using [FontStyle], [variant] using
1054 * [FontVariant], and [lineHeight] extra space (leading) around the font in
1055 * pixels, if not specified it's 1.2 the font size.
1056 */
1057 const Font({this.size, this.family, this.weight, this.style, this.variant,
1058 this.lineHeight});
1059
1060 /**
1061 * Merge the two fonts and return the result. See [Style.merge] for
1062 * more information.
1063 */
1064 factory Font.merge(Font a, Font b) {
1065 if (a == null) return b;
1066 if (b == null) return a;
1067 return new Font._merge(a, b);
1068 }
1069
1070 Font._merge(Font a, Font b)
1071 : size = _mergeVal(a.size, b.size),
1072 family = _mergeVal(a.family, b.family),
1073 weight = _mergeVal(a.weight, b.weight),
1074 style = _mergeVal(a.style, b.style),
1075 variant = _mergeVal(a.variant, b.variant),
1076 lineHeight = _mergeVal(a.lineHeight, b.lineHeight);
1077
1078 /**
1079 * Shorthand CSS format for font is:
1080 *
1081 * font-style font-variant font-weight font-size/line-height font-family
1082 *
1083 * The font-size and font-family values are required. If any of the other
1084 * values are missing the default value is used.
1085 */
1086 String get cssExpression {
1087 // TODO(jimhug): include variant, style, other options
1088 if (weight != null) {
1089 // TODO(jacobr): is this really correct for lineHeight?
1090 if (lineHeight != null) {
1091 return "$weight ${size}px/$lineHeightInPixels $_fontsAsString";
1092 }
1093 return '$weight ${size}px $_fontsAsString';
1094 }
1095
1096 return '${size}px $_fontsAsString';
1097 }
1098
1099 Font scale(num ratio) =>
1100 new Font(size: size * ratio, family: family, weight: weight, style: style,
1101 variant: variant);
1102
1103 /**
1104 * The lineHeight, provides an indirect means to specify the leading. The
1105 * leading is the difference between the font-size height and the (used)
1106 * value of line height in pixels. If lineHeight is not specified it's
1107 * automatically computed as 1.2 of the font size. Firefox is 1.2, Safari is
1108 * ~1.2, and CSS suggest a ration from 1 to 1.2 of the font-size when
1109 * computing line-height. The Font class constructor has the computation for
1110 * _lineHeight.
1111 */
1112 num get lineHeightInPixels {
1113 if (lineHeight != null) {
1114 if (lineHeight.inPixels) {
1115 return lineHeight.height;
1116 } else {
1117 return (size != null) ? lineHeight.height * size : null;
1118 }
1119 } else {
1120 return (size != null) ? size * 1.2 : null;
1121 }
1122 }
1123
1124 int get hashCode {
1125 // TODO(jimhug): Lot's of potential collisions here. List of fonts, etc.
1126 return size.toInt() % family[0].hashCode;
1127 }
1128
1129 bool operator ==(Object other) {
1130 if (other is! Font) return false;
1131 Font o = other;
1132 return o.size == size && o.family == family && o.weight == weight &&
1133 o.lineHeight == lineHeight && o.style == style && o.variant == variant;
1134 }
1135
1136 // TODO(terry): This is fragile should probably just iterate through the list
1137 // of fonts construction the font-family string.
1138 /** Return fonts as a comma seperated list sans the square brackets. */
1139 String get _fontsAsString {
1140 String fonts = family.toString();
1141 return fonts.length > 2 ? fonts.substring(1, fonts.length - 1) : "";
1142 }
1143 }
1144
1145 /**
1146 * This class stores the sizes of the box edges in the CSS [box model][]. Each
1147 * edge area is placed around the sides of the content box. The innermost area
1148 * is the [Style.padding] area which has a background and surrounds the content.
1149 * The content and padding area is surrounded by the [Style.border], which
1150 * itself is surrounded by the transparent [Style.margin]. This box represents
1151 * the eges of padding, border, or margin depending on which accessor was used
1152 * to retrieve it.
1153 *
1154 * [box model]: https://developer.mozilla.org/en/CSS/box_model
1155 */
1156 class BoxEdge {
1157 /** The size of the left edge, or null if the style has no edge. */
1158 final num left;
1159
1160 /** The size of the top edge, or null if the style has no edge. */
1161 final num top;
1162
1163 /** The size of the right edge, or null if the style has no edge. */
1164 final num right;
1165
1166 /** The size of the bottom edge, or null if the style has no edge. */
1167 final num bottom;
1168
1169 /**
1170 * Creates a box edge with the specified [left], [top], [right], and
1171 * [bottom] width.
1172 */
1173 const BoxEdge([this.left, this.top, this.right, this.bottom]);
1174
1175 /**
1176 * Creates a box edge with the specified [top], [right], [bottom], and
1177 * [left] width. This matches the typical CSS order:
1178 * <https://developer.mozilla.org/en/CSS/margin>
1179 * <https://developer.mozilla.org/en/CSS/border-width>
1180 * <https://developer.mozilla.org/en/CSS/padding>.
1181 */
1182 const BoxEdge.clockwiseFromTop(this.top, this.right, this.bottom, this.left);
1183
1184 /**
1185 * This is a helper to creates a box edge with the same [left], [top]
1186 * [right], and [bottom] widths.
1187 */
1188 const BoxEdge.uniform(num size)
1189 : top = size, left = size, bottom = size, right = size;
1190
1191 /**
1192 * Takes a possibly null box edge, with possibly null metrics, and fills
1193 * them in with 0 instead.
1194 */
1195 factory BoxEdge.nonNull(BoxEdge other) {
1196 if (other == null) return const BoxEdge(0, 0, 0, 0);
1197 num left = other.left;
1198 num top = other.top;
1199 num right = other.right;
1200 num bottom = other.bottom;
1201 bool make = false;
1202 if (left == null) {
1203 make = true;
1204 left = 0;
1205 }
1206 if (top == null) {
1207 make = true;
1208 top = 0;
1209 }
1210 if (right == null) {
1211 make = true;
1212 right = 0;
1213 }
1214 if (bottom == null) {
1215 make = true;
1216 bottom = 0;
1217 }
1218 return make ? new BoxEdge(left, top, right, bottom) : other;
1219 }
1220
1221 /**
1222 * Merge the two box edge sizes and return the result. See [Style.merge] for
1223 * more information.
1224 */
1225 factory BoxEdge.merge(BoxEdge x, BoxEdge y) {
1226 if (x == null) return y;
1227 if (y == null) return x;
1228 return new BoxEdge._merge(x, y);
1229 }
1230
1231 BoxEdge._merge(BoxEdge x, BoxEdge y)
1232 : left = _mergeVal(x.left, y.left),
1233 top = _mergeVal(x.top, y.top),
1234 right = _mergeVal(x.right, y.right),
1235 bottom = _mergeVal(x.bottom, y.bottom);
1236
1237 /**
1238 * The total size of the horizontal edges. Equal to [left] + [right], where
1239 * null is interpreted as 0px.
1240 */
1241 num get width => (left != null ? left : 0) + (right != null ? right : 0);
1242
1243 /**
1244 * The total size of the vertical edges. Equal to [top] + [bottom], where
1245 * null is interpreted as 0px.
1246 */
1247 num get height => (top != null ? top : 0) + (bottom != null ? bottom : 0);
1248 }
1249
1250 _mergeVal(x, y) => y != null ? y : x;
OLDNEW
« no previous file with comments | « pkg/csslib/lib/src/options.dart ('k') | pkg/csslib/lib/src/token.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698