CamelCase node kinds. Also fix shape glue problem in CSS color comparison.

This commit is contained in:
Patrick Walton 2012-06-14 16:38:05 -07:00
parent d2455c7bb6
commit c348486140
8 changed files with 130 additions and 130 deletions

View file

@ -11,11 +11,11 @@ enum node_data = {
}; };
enum node_kind { enum node_kind {
nk_element(element), Element(ElementData),
nk_text(str) Text(str)
} }
class element { class ElementData {
let tag_name: str; let tag_name: str;
let subclass: ~element_subclass; let subclass: ~element_subclass;
let attrs: dvec<~attr>; let attrs: dvec<~attr>;
@ -34,7 +34,8 @@ class element {
} }
i += 1u; i += 1u;
} }
ret none;
none
} }
} }

View file

@ -1,6 +1,6 @@
#[doc="Fundamental layout structures and algorithms."] #[doc="Fundamental layout structures and algorithms."]
import dom::base::{element, es_div, es_img, node_data, node_kind, node}; import dom::base::{Element, es_div, es_img, node_data, node_kind, node};
import dom::rcu; import dom::rcu;
import dom::rcu::reader_methods; import dom::rcu::reader_methods;
import gfx::geometry; import gfx::geometry;
@ -145,8 +145,8 @@ impl node_methods for node {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
import dom::base::{element, es_div, es_img, methods, nk_element, node_data, import dom::base::{ElementData, es_div, es_img, methods, Element, node_data, node_kind, node};
node_kind, node, wr_tree_ops}; import dom::base::{wr_tree_ops};
import dom::rcu::scope; import dom::rcu::scope;
import box_builder::{box_builder_methods}; import box_builder::{box_builder_methods};
@ -183,10 +183,10 @@ mod test {
~es_img({mut size: size}) ~es_img({mut size: size})
} }
let n0 = s.new_node(nk_element(element("img", mk_img(Size2D(au(10),au(10)))))); let n0 = s.new_node(Element(ElementData("img", mk_img(Size2D(au(10),au(10))))));
let n1 = s.new_node(nk_element(element("img", mk_img(Size2D(au(10),au(10)))))); let n1 = s.new_node(Element(ElementData("img", mk_img(Size2D(au(10),au(10))))));
let n2 = s.new_node(nk_element(element("img", mk_img(Size2D(au(10),au(20)))))); let n2 = s.new_node(Element(ElementData("img", mk_img(Size2D(au(10),au(20))))));
let n3 = s.new_node(nk_element(element("div", ~es_div))); let n3 = s.new_node(Element(ElementData("div", ~es_div)));
tree::add_child(s, n3, n0); tree::add_child(s, n3, n0);
tree::add_child(s, n3, n1); tree::add_child(s, n3, n1);

View file

@ -1,6 +1,6 @@
#[doc="Creates CSS boxes from a DOM."] #[doc="Creates CSS boxes from a DOM."]
import dom::base::{element, es_div, es_img, nk_element, nk_text, node}; import dom::base::{ElementData, es_div, es_img, Element, Text, node};
import dom::style::{display_type, di_block, di_inline, di_none}; import dom::style::{display_type, di_block, di_inline, di_none};
import dom::rcu::reader_methods; import dom::rcu::reader_methods;
import gfx::geometry; import gfx::geometry;
@ -20,8 +20,7 @@ enum ctxt = {
// The parent box that these boxes will be added to. // The parent box that these boxes will be added to.
parent_box: @box, parent_box: @box,
// The current anonymous box that we're currently appending inline nodes // The current anonymous box that we're currently appending inline nodes to.
// to.
// //
// See CSS2 9.2.1.1. // See CSS2 9.2.1.1.
mut anon_box: option<@box> mut anon_box: option<@box>
@ -61,8 +60,7 @@ impl methods for ctxt {
self.finish_anonymous_box_if_necessary(); self.finish_anonymous_box_if_necessary();
} }
// Add the child's box to the current enclosing box or the current // Add the child's box to the current enclosing box or the current anonymous box.
// anonymous box.
alt kid.get_computed_style().display { alt kid.get_computed_style().display {
di_block { di_block {
btree.add_child(self.parent_box, kid_box); btree.add_child(self.parent_box, kid_box);
@ -70,11 +68,14 @@ impl methods for ctxt {
di_inline { di_inline {
let anon_box = alt self.anon_box { let anon_box = alt self.anon_box {
none { none {
// the anonymous box inherits the attributes //
// of its parents for now, so that // the anonymous box inherits the attributes of its parents for now, so
// properties of intrinsic boxes are not // that properties of intrinsic boxes are not spread to their parenting
// spread to their parenting anonymous box. // anonymous box.
//
// TODO: check what css actually specifies // TODO: check what css actually specifies
//
let b = new_box(self.parent_node, bk_inline); let b = new_box(self.parent_node, bk_inline);
self.anon_box = some(b); self.anon_box = some(b);
b b
@ -159,8 +160,8 @@ impl box_builder_priv for node {
"] "]
fn determine_box_kind() -> box_kind { fn determine_box_kind() -> box_kind {
alt self.rd({ |n| copy n.kind }) { alt self.rd({ |n| copy n.kind }) {
~nk_text(string) { bk_text(@text_box(string)) } ~Text(string) { bk_text(@text_box(string)) }
~nk_element(element) { ~Element(element) {
alt *element.subclass { alt *element.subclass {
es_div { bk_block } es_div { bk_block }
es_img({size}) { bk_intrinsic(@size) } es_img({size}) { bk_intrinsic(@size) }

View file

@ -1,5 +1,5 @@
#[doc="Applies style to boxes."] #[doc="Applies style to boxes."]
import dom::base::{es_img, nk_element, node}; import dom::base::{es_img, Element, node};
import dom::rcu::reader_methods; import dom::rcu::reader_methods;
import image::base::load; import image::base::load;
import layout::base::*; import layout::base::*;
@ -20,11 +20,11 @@ impl apply_style_methods for @box {
self.node.rd { self.node.rd {
|node| |node|
alt node.kind { alt node.kind {
~nk_element(element) { ~Element(element) {
let style = self.node.get_computed_style(); let style = self.node.get_computed_style();
self.appearance.background_color = some(style.back_color); self.appearance.background_color = some(style.back_color);
alt element.subclass { alt element.subclass {
~es_img(*) { ~es_img(*) {
alt element.get_attr("src") { alt element.get_attr("src") {
@ -34,8 +34,7 @@ impl apply_style_methods for @box {
// FIXME: Don't load synchronously! // FIXME: Don't load synchronously!
#debug("loading image from %s", url); #debug("loading image from %s", url);
let image = @load(url); let image = @load(url);
self.appearance.background_image = self.appearance.background_image = some(image);
some(image);
} }
none { none {
/* Ignore. */ /* Ignore. */

View file

@ -1,17 +1,16 @@
#[doc="Perform css selector matching"] #[doc="Perform css selector matching"]
import dom::base::{node, nk_element, nk_text}; import dom::base::{node, Element, ElementData, Text};
import dom::style::{selector, style_decl, font_size, display, text_color, import dom::style::{selector, style_decl, font_size, display, text_color, background_color,
background_color, stylesheet, element, child, descendant, stylesheet, element, child, descendant, sibling, attr, exact, exists, includes,
sibling, attr, exact, exists, includes, starts_with}; starts_with};
import dom::rcu::{reader_methods}; import dom::rcu::{reader_methods};
import style::{computed_style, default_style_for_node_kind}; import style::{computed_style, default_style_for_node_kind};
import base::{layout_data}; import base::{layout_data};
export matching_methods; export matching_methods;
#[doc="Update the computed style of an html element with a style specified #[doc="Update the computed style of an HTML element with a style specified by CSS."]
by css."]
fn update_style(style : @computed_style, decl : style_decl) { fn update_style(style : @computed_style, decl : style_decl) {
alt decl { alt decl {
display(dis) { (*style).display = dis; } display(dis) { (*style).display = dis; }
@ -20,8 +19,8 @@ fn update_style(style : @computed_style, decl : style_decl) {
} }
} }
#[doc="Check if a css attribute matches the attribute of an html element."] #[doc="Check if a CSS attribute matches the attribute of an HTML element."]
fn attrs_match(attr : attr, elmt : dom::base::element) -> bool { fn attrs_match(attr: attr, elmt: ElementData) -> bool {
alt attr { alt attr {
exists(name) { exists(name) {
alt elmt.get_attr(name) { alt elmt.get_attr(name) {
@ -56,22 +55,25 @@ fn attrs_match(attr : attr, elmt : dom::base::element) -> bool {
if value.len() == val.len() { ret true; } if value.len() == val.len() { ret true; }
else { ret value.starts_with(val + "-"); } else { ret value.starts_with(val + "-"); }
} }
none { ret false; } none {
ret false;
}
} }
} }
} }
} }
impl priv_matching_methods for node { impl priv_matching_methods for node {
#[doc="Checks if the given css selector, which must describe a single #[doc="
element with no relational information, describes the given Checks if the given CSS selector, which must describe a single element with no relational
html element."] information, describes the given HTML element.
fn matches_element(sel : ~selector) -> bool { "]
fn matches_element(sel: ~selector) -> bool {
alt *sel { alt *sel {
child(_, _) | descendant(_, _) | sibling(_, _) { ret false; } child(_, _) | descendant(_, _) | sibling(_, _) { ret false; }
element(tag, attrs) { element(tag, attrs) {
alt self.rd { |n| copy *n.kind } { alt self.rd { |n| copy *n.kind } {
nk_element(elmt) { Element(elmt) {
if !(tag == "*" || tag == elmt.tag_name) { if !(tag == "*" || tag == elmt.tag_name) {
ret false; ret false;
} }
@ -84,7 +86,7 @@ impl priv_matching_methods for node {
ret true; ret true;
} }
nk_text(str) { /*fall through, currently unsupported*/ } Text(str) { /*fall through, currently unsupported*/ }
} }
} }
} }
@ -93,7 +95,7 @@ impl priv_matching_methods for node {
//unsupported. //unsupported.
} }
#[doc = "Checks if a generic css selector matches a given html element"] #[doc = "Checks if a generic CSS selector matches a given HTML element"]
fn matches_selector(sel : ~selector) -> bool { fn matches_selector(sel : ~selector) -> bool {
alt *sel { alt *sel {
element(str, atts) { ret self.matches_element(sel); } element(str, atts) { ret self.matches_element(sel); }
@ -202,17 +204,16 @@ impl matching_methods for node {
} }
mod test { mod test {
import dom::base::{node_scope, methods, nk_element, attr, es_div, import dom::base::{node_scope, methods, Element, attr, es_div, es_img, es_unknown, es_head};
es_img, es_unknown, es_head, wr_tree_ops}; import dom::base::{wr_tree_ops};
import dvec::{dvec, extensions}; import dvec::{dvec, extensions};
import io::println; import io::println;
fn new_node_from_attr(scope : node_scope, -name : str, -val : str) -> node fn new_node_from_attr(scope: node_scope, -name: str, -val: str) -> node {
{ let elmt = ElementData("div", ~es_div);
let elmt = dom::base::element("div", ~es_div);
let attr = ~attr(name, val); let attr = ~attr(name, val);
elmt.attrs.push(attr); elmt.attrs.push(attr);
ret scope.new_node(nk_element(elmt)); ret scope.new_node(Element(elmt));
} }
#[test] #[test]
@ -222,7 +223,7 @@ mod test {
let sel = element("*", [starts_with("lang", "en")]); let sel = element("*", [starts_with("lang", "en")]);
assert node.matches_selector(~sel); assert node.matches_selector(~sel);
} }
#[test] #[test]
@ -232,7 +233,7 @@ mod test {
let sel = element("*", [starts_with("lang", "en")]); let sel = element("*", [starts_with("lang", "en")]);
assert node.matches_selector(~sel); assert node.matches_selector(~sel);
} }
#[test] #[test]
@ -242,7 +243,7 @@ mod test {
let sel = element("*", [starts_with("lang", "en")]); let sel = element("*", [starts_with("lang", "en")]);
assert !node.matches_selector(~sel); assert !node.matches_selector(~sel);
} }
#[test] #[test]

View file

@ -1,9 +1,7 @@
#[doc="High-level interface to CSS selector matching."] #[doc="High-level interface to CSS selector matching."]
import dom::style::{display_type, di_block, di_inline, di_none, import dom::style::{display_type, di_block, di_inline, di_none, stylesheet};
stylesheet}; import dom::base::{es_div, es_head, es_img, Element, Text, node};
import dom::base::{element, es_div, es_head, es_img, nk_element, nk_text};
import dom::base::{node};
import dom::base::node_kind; import dom::base::node_kind;
import dom::rcu::reader_methods; import dom::rcu::reader_methods;
import layout::base::*; // FIXME: resolve bug requires * import layout::base::*; // FIXME: resolve bug requires *
@ -17,21 +15,20 @@ type computed_style = {mut display : display_type,
#[doc="Returns the default style for the given node kind."] #[doc="Returns the default style for the given node kind."]
fn default_style_for_node_kind(kind: node_kind) -> computed_style { fn default_style_for_node_kind(kind: node_kind) -> computed_style {
alt kind { alt kind {
nk_text(*) { Text(*) {
{mut display: di_inline, {mut display: di_inline,
mut back_color : white()} mut back_color: white()}
} }
nk_element(element) { Element(element) {
let r = rand::rng(); let r = rand::rng();
let rand_color = rgb(r.next() as u8, r.next() as u8, r.next() as u8); let rand_color = rgb(r.next() as u8, r.next() as u8, r.next() as u8);
alt *element.subclass { alt *element.subclass {
es_div { {mut display : di_block, es_div { {mut display: di_block,
mut back_color : rand_color} } mut back_color: rand_color} }
es_head { {mut display : di_none, mut back_color : rand_color} } es_head { {mut display: di_none, mut back_color: rand_color} }
es_img(*) { {mut display : di_inline, mut back_color : rand_color} } es_img(*) { {mut display: di_inline, mut back_color: rand_color} }
es_unknown { {mut display : di_inline, mut back_color : es_unknown { {mut display: di_inline, mut back_color: rand_color} }
rand_color} }
} }
} }
} }
@ -41,9 +38,8 @@ impl style_priv for node {
#[doc=" #[doc="
Performs CSS selector matching on a node. Performs CSS selector matching on a node.
This is, importantly, the function that creates the layout data for This is, importantly, the function that creates the layout data for the node (the reader-
the node (the reader-auxiliary box in the RCU model) and populates it auxiliary box in the RCU model) and populates it with the computed style.
with the computed style.
"] "]
fn recompute_style(styles : stylesheet) { fn recompute_style(styles : stylesheet) {
let style = self.match_css_style(styles); let style = self.match_css_style(styles);
@ -63,8 +59,8 @@ impl style_priv for node {
impl style_methods for node { impl style_methods for node {
#[doc=" #[doc="
Returns the computed style for the given node. If CSS selector matching Returns the computed style for the given node. If CSS selector matching has not yet been
has not yet been performed, fails. performed, fails.
TODO: Return a safe reference; don't copy. TODO: Return a safe reference; don't copy.
"] "]
@ -78,9 +74,8 @@ impl style_methods for node {
#[doc=" #[doc="
Performs CSS selector matching on a subtree. Performs CSS selector matching on a subtree.
This is, importantly, the function that creates the layout data for This is, importantly, the function that creates the layout data for the node (the reader-
the node (the reader-auxiliary box in the RCU model) and populates it auxiliary box in the RCU model) and populates it with the computed style.
with the computed style.
TODO: compute the style of multiple nodes in parallel. TODO: compute the style of multiple nodes in parallel.
"] "]

View file

@ -1,9 +1,8 @@
#[doc="Constructs a DOM tree from an incoming token stream."] #[doc="Constructs a DOM tree from an incoming token stream."]
import dom::rcu::writer_methods; import dom::rcu::writer_methods;
import dom::base::{attr, element, element_subclass, es_div, es_head, es_img}; import dom::base::{attr, element_subclass, es_div, es_head, es_img, es_unknown, methods, Element};
import dom::base::{es_unknown, methods, nk_element, nk_text, rd_tree_ops}; import dom::base::{ElementData, Text, rd_tree_ops, wr_tree_ops};
import dom::base::{wr_tree_ops};
import dom = dom::base; import dom = dom::base;
import dvec::extensions; import dvec::extensions;
import geom::size::Size2D; import geom::size::Size2D;
@ -12,14 +11,12 @@ import gfx::geometry::au;
import parser = parser::lexer::html; import parser = parser::lexer::html;
import parser::token; import parser::token;
fn link_up_attribute(scope: dom::node_scope, node: dom::node, -key: str, fn link_up_attribute(scope: dom::node_scope, node: dom::node, -key: str, -value: str) {
-value: str) { // TODO: Implement atoms so that we don't always perform string comparisons.
// TODO: Implement atoms so that we don't always perform string
// comparisons.
scope.rd(node) { scope.rd(node) {
|node_contents| |node_contents|
alt *node_contents.kind { alt *node_contents.kind {
dom::nk_element(element) { Element(element) {
element.attrs.push(~attr(copy key, copy value)); element.attrs.push(~attr(copy key, copy value));
alt *element.subclass { alt *element.subclass {
es_img(img) if key == "width" { es_img(img) if key == "width" {
@ -45,7 +42,8 @@ fn link_up_attribute(scope: dom::node_scope, node: dom::node, -key: str,
} }
} }
} }
dom::nk_text(*) {
Text(*) {
fail "attempt to link up an attribute to a text node" fail "attempt to link up an attribute to a text node"
} }
} }
@ -66,10 +64,9 @@ fn build_element_subclass(tag_name: str) -> ~element_subclass {
} }
} }
fn build_dom(scope: dom::node_scope, fn build_dom(scope: dom::node_scope, stream: port<token>) -> dom::node {
stream: port<token>) -> dom::node {
// The current reference node. // The current reference node.
let mut cur = scope.new_node(dom::nk_element(element("html", ~es_div))); let mut cur = scope.new_node(Element(ElementData("html", ~es_div)));
loop { loop {
let token = stream.recv(); let token = stream.recv();
alt token { alt token {
@ -77,9 +74,7 @@ fn build_dom(scope: dom::node_scope,
parser::to_start_opening_tag(tag_name) { parser::to_start_opening_tag(tag_name) {
#debug["starting tag %s", tag_name]; #debug["starting tag %s", tag_name];
let element_subclass = build_element_subclass(tag_name); let element_subclass = build_element_subclass(tag_name);
let new_node = let new_node = scope.new_node(Element(ElementData(tag_name, element_subclass)));
scope.new_node(dom::nk_element(element(tag_name,
element_subclass)));
scope.add_child(cur, new_node); scope.add_child(cur, new_node);
cur = new_node; cur = new_node;
} }
@ -98,7 +93,7 @@ fn build_dom(scope: dom::node_scope,
} }
parser::to_text(s) if !s.is_whitespace() { parser::to_text(s) if !s.is_whitespace() {
let s <- s; let s <- s;
let new_node = scope.new_node(dom::nk_text(s)); let new_node = scope.new_node(Text(s));
scope.add_child(cur, new_node); scope.add_child(cur, new_node);
} }
parser::to_text(_) { parser::to_text(_) {

View file

@ -7,9 +7,17 @@
import float::round; import float::round;
import libc::types::os::arch::c95::c_double; import libc::types::os::arch::c95::c_double;
import css_colors::*; import css_colors::*;
import cmp::eq;
enum Color = {red : u8, green : u8, blue : u8, alpha : float}; enum Color = {red : u8, green : u8, blue : u8, alpha : float};
impl Color of eq for Color {
fn eq(&&other: Color) -> bool {
ret self.red == other.red && self.green == other.green && self.blue == other.blue &&
self.alpha == other.alpha;
}
}
fn rgba(r : u8, g : u8, b : u8, a : float) -> Color { fn rgba(r : u8, g : u8, b : u8, a : float) -> Color {
Color({red : r, green : g, blue : b, alpha : a}) Color({red : r, green : g, blue : b, alpha : a})
} }
@ -172,55 +180,55 @@ mod test {
#[test] #[test]
fn test_parse_by_name() { fn test_parse_by_name() {
assert red() == parse_color("red"); assert red().eq(parse_color("red"));
assert lime() == parse_color("Lime"); assert lime().eq(parse_color("Lime"));
assert blue() == parse_color("BLUE"); assert blue().eq(parse_color("BLUE"));
assert green() == parse_color("GreEN"); assert green().eq(parse_color("GreEN"));
assert white() == parse_color("white"); assert white().eq(parse_color("white"));
assert black() == parse_color("Black"); assert black().eq(parse_color("Black"));
assert gray() == parse_color("Gray"); assert gray().eq(parse_color("Gray"));
assert silver() == parse_color("SiLvEr"); assert silver().eq(parse_color("SiLvEr"));
assert maroon() == parse_color("maroon"); assert maroon().eq(parse_color("maroon"));
assert purple() == parse_color("PURPLE"); assert purple().eq(parse_color("PURPLE"));
assert fuschia() == parse_color("FUSCHIA"); assert fuschia().eq(parse_color("FUSCHIA"));
assert olive() == parse_color("oLiVe"); assert olive().eq(parse_color("oLiVe"));
assert yellow() == parse_color("yellow"); assert yellow().eq(parse_color("yellow"));
assert navy() == parse_color("NAVY"); assert navy().eq(parse_color("NAVY"));
assert teal() == parse_color("Teal"); assert teal().eq(parse_color("Teal"));
assert aqua() == parse_color("Aqua"); assert aqua().eq(parse_color("Aqua"));
} }
#[test] #[test]
fn test_parsing_rgb() { fn test_parsing_rgb() {
assert red() == parse_color("rgb(255,0,0)"); assert red().eq(parse_color("rgb(255,0,0)"));
assert red() == parse_color("rgba(255,0,0,1.0)"); assert red().eq(parse_color("rgba(255,0,0,1.0)"));
assert red() == parse_color("rgba(255,0,0,1)"); assert red().eq(parse_color("rgba(255,0,0,1)"));
assert lime() == parse_color("rgba(0,255,0,1.00)"); assert lime().eq(parse_color("rgba(0,255,0,1.00)"));
assert rgb(1u8,2u8,3u8) == parse_color("rgb(1,2,03)"); assert rgb(1u8,2u8,3u8).eq(parse_color("rgb(1,2,03)"));
assert rgba(15u8,250u8,3u8,0.5) == parse_color("rgba(15,250,3,.5)"); assert rgba(15u8,250u8,3u8,0.5).eq(parse_color("rgba(15,250,3,.5)"));
assert rgba(15u8,250u8,3u8,0.5) == parse_color("rgba(15,250,3,0.5)"); assert rgba(15u8,250u8,3u8,0.5).eq(parse_color("rgba(15,250,3,0.5)"));
} }
#[test] #[test]
fn test_parsing_hsl() { fn test_parsing_hsl() {
assert red() == parse_color("hsl(0,1,.5)"); assert red().eq(parse_color("hsl(0,1,.5)"));
assert lime() == parse_color("hsl(120.0,1.0,.5)"); assert lime().eq(parse_color("hsl(120.0,1.0,.5)"));
assert blue() == parse_color("hsl(240.0,1.0,.5)"); assert blue().eq(parse_color("hsl(240.0,1.0,.5)"));
assert green() == parse_color("hsl(120.0,1.0,.25)"); assert green().eq(parse_color("hsl(120.0,1.0,.25)"));
assert white() == parse_color("hsl(1.0,1.,1.0)"); assert white().eq(parse_color("hsl(1.0,1.,1.0)"));
assert white() == parse_color("hsl(129.0,0.3,1.0)"); assert white().eq(parse_color("hsl(129.0,0.3,1.0)"));
assert black() == parse_color("hsl(231.2,0.75,0.0)"); assert black().eq(parse_color("hsl(231.2,0.75,0.0)"));
assert black() == parse_color("hsl(11.2,0.0,0.0)"); assert black().eq(parse_color("hsl(11.2,0.0,0.0)"));
assert gray() == parse_color("hsl(0.0,0.0,0.5)"); assert gray().eq(parse_color("hsl(0.0,0.0,0.5)"));
assert maroon() == parse_color("hsl(0.0,1.0,0.25)"); assert maroon().eq(parse_color("hsl(0.0,1.0,0.25)"));
assert purple() == parse_color("hsl(300.0,1.0,0.25)"); assert purple().eq(parse_color("hsl(300.0,1.0,0.25)"));
assert fuschia() == parse_color("hsl(300,1.0,0.5)"); assert fuschia().eq(parse_color("hsl(300,1.0,0.5)"));
assert olive() == parse_color("hsl(60.,1.0,0.25)"); assert olive().eq(parse_color("hsl(60.,1.0,0.25)"));
assert yellow() == parse_color("hsl(60.,1.0,0.5)"); assert yellow().eq(parse_color("hsl(60.,1.0,0.5)"));
assert navy() == parse_color("hsl(240.0,1.0,.25)"); assert navy().eq(parse_color("hsl(240.0,1.0,.25)"));
assert teal() == parse_color("hsl(180.0,1.0,.25)"); assert teal().eq(parse_color("hsl(180.0,1.0,.25)"));
assert aqua() == parse_color("hsl(180.0,1.0,.5)"); assert aqua().eq(parse_color("hsl(180.0,1.0,.5)"));
} }
} }