diff --git a/lib/CSS/TagSet.rakumod b/lib/CSS/TagSet.rakumod index 863e0ea..d8043c5 100644 --- a/lib/CSS/TagSet.rakumod +++ b/lib/CSS/TagSet.rakumod @@ -1,69 +1,64 @@ -use v6; - -# interface role for tagsets -role CSS::TagSet:ver<0.1.2> { - use CSS::Properties; - use CSS::Stylesheet; - use CSS::Writer; - - sub load-css-tagset($tag-css, |c) is export(:load-css-tagset) { - my %asts; - with $tag-css { - # Todo: load via CSS::Stylesheet? - my CSS::Module $module = CSS::Module::CSS3.module; - my $actions = $module.actions.new: |c; - my $p = $module.grammar.parsefile(.IO.absolute, :$actions); - my %ast = $p.ast; - - for %ast.list { - with . { - my $declarations = .; - for ..list { - given . { - my @path; - for .list { - for ..list { - @path.push: $_ - with .; - } +#| interface role for tagsets +unit role CSS::TagSet:ver<0.1.2>; + +use CSS::Properties; +use CSS::Stylesheet; +use CSS::Writer; + +sub load-css-tagset($tag-css, |c) is export(:load-css-tagset) { + my %asts; + with $tag-css { + # Todo: load via CSS::Stylesheet? + my $css = .IO.slurp; + my CSS::Stylesheet $style-sheet .= parse: $css, |c; + + for $style-sheet.rules { + with .ast { + my $declarations = .; + for ..list { + given . { + my @path; + for .list { + for ..list { + @path.push: $_ + with .; } - - my $key = @path == 1 ?? @path.head !! CSS::Writer.write: :selector($_); - %asts{$key}.append: $declarations.list; } + + my $key = @path == 1 ?? @path.head !! CSS::Writer.write: :selector($_); + %asts{$key}.append: $declarations.map: {:property($_)}; } } } } - else { - note "running with 'raku --doc', I hope" - } - - %asts; } - - method xpath-init($) {} # override me - method stylesheet-content($) { [] } # override me - method module { ... } - method stylesheet($doc, :$media, Bool :$links = False, |c --> CSS::Stylesheet) { - my @styles = @.stylesheet-content($doc, :$media, :$links); - my CSS::Stylesheet $css .= new: :$media, |c; - $css.parse($_) for @styles; - $css; + else { + note "running with 'raku --doc', I hope" } - # attribute that contains inline styling - method inline-style-attribute { 'style' } + %asts; +} - # method to extract inline styling - method inline-style(Str $, Str :$style) { - CSS::Properties.new(:$.module, :$style); - } +method xpath-init($) {} # override me +method stylesheet-content($) { [] } # override me +method module { ... } +method stylesheet($doc, :$media, Bool :$links = False, |c --> CSS::Stylesheet) { + my @styles = @.stylesheet-content($doc, :$media, :$links); + my CSS::Stylesheet $css .= new: :$media, |c; + $css.parse($_) for @styles; + $css; +} - method base-style(|c) { ... } +# attribute that contains inline styling +method inline-style-attribute { 'style' } +# method to extract inline styling +method inline-style(Str $, Str :$style) { + CSS::Properties.new(:$.module, :$style); } +method base-style(|c) { ... } + =begin pod =head2 Name diff --git a/lib/CSS/TagSet/Pango.rakumod b/lib/CSS/TagSet/Pango.rakumod index 312fcff..310d4ff 100644 --- a/lib/CSS/TagSet/Pango.rakumod +++ b/lib/CSS/TagSet/Pango.rakumod @@ -1,122 +1,121 @@ -use v6; +unit class CSS::TagSet::Pango; use CSS::TagSet :&load-css-tagset; +also does CSS::TagSet; -class CSS::TagSet::Pango does CSS::TagSet { - use CSS::Module; - use CSS::Module::CSS3; - use CSS::Properties; - use CSS::Units; +use CSS::Module; +use CSS::Module::CSS3; +use CSS::Properties; +use CSS::Units; - has CSS::Module $.module = CSS::Module::CSS3.module; - has CSS::Properties %!props; +has CSS::Module $.module = CSS::Module::CSS3.module; +has CSS::Properties %!props; - constant %Tags is export(:PangoTags) = load-css-tagset(%?RESOURCES); - method declarations { %Tags } +constant %Tags is export(:PangoTags) = load-css-tagset(%?RESOURCES); +method declarations { %Tags } - method base-style(Str $prop) { - %!props{$prop} //= CSS::Properties.new(:$!module, declarations => %Tags{$prop}) // []; - } - - # mapping of Pango attributes to CSS properties - has %!SpanProp = %( - background => 'background-color', - 'face'|'font_family' => 'font-family', - foreground => 'color', - stretch => 'font-stretch', - style => 'font-style', - weight => 'font-weight', - ); - submethod TWEAK { +method base-style(Str $prop) { + %!props{$prop} //= CSS::Properties.new(:$!module, declarations => %Tags{$prop}) // []; +} - my %CustomProps = %( - rise => '-pango-rise' => %( - :synopsis, - :default(0), - :coerce(-> Int:D() $num { :$num }), - ), - fallback => '-pango-fallback' => %( - :synopsis('True | False'), - :default, - :coerce(-> Str:D $att { my $num = ($att ~~ /:i 'True'/) ?? 1 !! 0; :$num }), - ), - size => 'font-size' => %( - # map Pango `size` attribute to CSS `font-size` property - :like, - :synopsis(" | xx-small | x-small | small | medium | large | x-large | xx-large | smaller | larger"), - :coerce( - -> $_ { - when CSS::Units:D { - $_; - } - when .lc ∈ set { - :keyw(.lc); - } - default { - # size in thousandths of a point - my $pt = .Numeric / 1000; - :$pt - } +# mapping of Pango attributes to CSS properties +has %!SpanProp = %( + background => 'background-color', + 'face'|'font_family' => 'font-family', + foreground => 'color', + stretch => 'font-stretch', + style => 'font-style', + weight => 'font-weight', +); +submethod TWEAK { + + my %CustomProps = %( + rise => '-pango-rise' => %( + :synopsis, + :default(0), + :coerce(-> Int:D() $num { :$num }), + ), + fallback => '-pango-fallback' => %( + :synopsis('True | False'), + :default, + :coerce(-> Str:D $att { my $num = ($att ~~ /:i 'True'/) ?? 1 !! 0; :$num }), + ), + size => 'font-size' => %( + # map Pango `size` attribute to CSS `font-size` property + :like, + :synopsis(" | xx-small | x-small | small | medium | large | x-large | xx-large | smaller | larger"), + :coerce( + -> $_ { + when CSS::Units:D { + $_; + } + when .lc ∈ set { + :keyw(.lc); + } + default { + # size in thousandths of a point + my $pt = .Numeric / 1000; + :$pt + } + }), + ), + variant => 'font-variant' => %( + :like, + :synopsis("normal | smallcaps"), + :coerce( + -> Str:D $att { + my $keyw = $att.lc; + $keyw ~~ s/smallcaps/small-caps/; + :$keyw + if $keyw ∈ set ; }), - ), - variant => 'font-variant' => %( - :like, - :synopsis("normal | smallcaps"), - :coerce( - -> Str:D $att { - my $keyw = $att.lc; - $keyw ~~ s/smallcaps/small-caps/; - :$keyw - if $keyw ∈ set ; - }), - ), - strikethrough => 'text-decoration' => %( - :like, - :synopsis("true | false"), - :coerce( - -> $_ { - if $_ ~~ CSS::Units { - $_; - } - else { - :keyw( - /:i true/ ?? 'line-through' !! 'none' - ) - } + ), + strikethrough => 'text-decoration' => %( + :like, + :synopsis("true | false"), + :coerce( + -> $_ { + if $_ ~~ CSS::Units { + $_; } - ), + else { + :keyw( + /:i true/ ?? 'line-through' !! 'none' + ) + } + } ), - ); - - for %CustomProps.pairs { - my $att := .key; - my Str $name := .value.key; - my Hash $meta := .value.value; - $!module.extend(:$name, |$meta); - %!SpanProp{$att} = $name; - } + ), + ); + + for %CustomProps.pairs { + my $att := .key; + my Str $name := .value.key; + my Hash $meta := .value.value; + $!module.extend(:$name, |$meta); + %!SpanProp{$att} = $name; } +} - # Builds CSS properties from an element from a tag name and attributes - multi method tag-style('span', *%attrs) { - my CSS::Properties $css = self.base-style('span').clone; - - for %attrs.keys { - if %!SpanProp{$_}:exists { - my $name = %!SpanProp{$_}; - $css."$name"() = %attrs{$_}; - } - else { - warn "ignoring 'style' attribute: '$_'"; - } - } +# Builds CSS properties from an element from a tag name and attributes +multi method tag-style('span', *%attrs) { + my CSS::Properties $css = self.base-style('span').clone; - $css; + for %attrs.keys { + if %!SpanProp{$_}:exists { + my $name = %!SpanProp{$_}; + $css."$name"() = %attrs{$_}; + } + else { + warn "ignoring 'style' attribute: '$_'"; + } } - multi method tag-style($tag) { - self.base-style($tag); - } + $css; +} + +multi method tag-style($tag) { + self.base-style($tag); } =begin pod diff --git a/lib/CSS/TagSet/TaggedPDF.rakumod b/lib/CSS/TagSet/TaggedPDF.rakumod index b416a1d..baea376 100644 --- a/lib/CSS/TagSet/TaggedPDF.rakumod +++ b/lib/CSS/TagSet/TaggedPDF.rakumod @@ -1,106 +1,104 @@ -use v6; +unit class CSS::TagSet::TaggedPDF; use CSS::TagSet :&load-css-tagset; +also does CSS::TagSet; -class CSS::TagSet::TaggedPDF does CSS::TagSet { - use CSS::Module; - use CSS::Module::CSS3; - use CSS::Properties; +use CSS::Module; +use CSS::Module::CSS3; +use CSS::Properties; - has CSS::Module $.module = CSS::Module::CSS3.module; - has CSS::Properties %!props; +has CSS::Module $.module = CSS::Module::CSS3.module; +has CSS::Properties %!props; - constant %Tags is export(:PDFTags) = load-css-tagset(%?RESOURCES, :xml); +constant %Tags is export(:PDFTags) = load-css-tagset(%?RESOURCES, :xml); - method declarations { %Tags } +method declarations { %Tags } - method base-style(Str $prop) { - %!props{$prop} //= CSS::Properties.new: :$!module, declarations => %Tags{$prop} // []; - } +method base-style(Str $prop) { + %!props{$prop} //= CSS::Properties.new: :$!module, declarations => %Tags{$prop} // []; +} - submethod TWEAK { - my %CustomProps = %( - '-pdf-space-before'|'-pdf-space-after'|'-pdf-start-indent'|'-pdf-end-indent' => %( - :synopsis, - :default(0e0), - :coerce(-> Num:D() $num { :$num }), - ), - ); - - for %CustomProps.pairs { - $!module.extend(:name(.key), |.value); - } +submethod TWEAK { + my %CustomProps = %( + '-pdf-space-before'|'-pdf-space-after'|'-pdf-start-indent'|'-pdf-end-indent' => %( + :synopsis, + :default(0e0), + :coerce(-> Num:D() $num { :$num }), + ), + ); + for %CustomProps.pairs { + $!module.extend(:name(.key), |.value); } - sub snake-case($s) { - $s.split(/>/)».lc.join: '-' - } +} - # mapping of Tagged PDF attributes to CSS properties - our %Layout = %( - 'FontFamily'|'FontSize'|'FontStyle'|'FontWeight'|'FontVariant'|'FontStretch' - => -> Str $prop, $v { snake-case($prop) => $v ~ 'pt' }, - # IS0 32000-1 Table 343 – Standard layout attributes common to all standard structure types - 'Placement' => :display{ :Block, :Inline }, - 'WritingMode' => :direction{ :LrTb, :RlTb }, - 'BackgroundColor'|'BorderColor'|'Color' => -> $_, $c { - .&snake-case => '#' ~ $c.split(' ').map({sprintf("%02x", (.Num * 255).round)}).join; - }, - 'BorderStyle' => -> Str $prop, Str $s { - snake-case($prop) => [ $s.split(' ')>>.lc ]; - }, - 'BorderThickness'|'Padding' => -> Str $prop, Str $s { - snake-case($prop) => [ $s.split(' ').map: -> $pt { :$pt } ]; - }, - 'TextIdent'|'Width'|'Height'|'LineHeight' => -> Str $prop, Num() $pt { - # approximate - snake-case($prop) => :$pt; - }, - 'TextAlign' => -> Str $prop, Str $s { - snake-case($prop) => $s.lc; - }, - 'TextDecorationType' => -> Str $, Str $s { - text-decoration => $s.lc; - }, - # Custom properties which don't map well to CSS standard properties - 'SpaceBefore'|'SpaceAfter'|'StartIndent'|'EndIndent' => -> Str $prop, Num() $pt { - '-pdf-' ~ snake-case($prop) => :$pt; - }, - # Todo: BBox BlockAlign InlineAlign TBorderStyle TPadding TextDecorationColor TextDecorationThickness RubyAlign RubyPosition GlyphOrientationVertical - ); +sub snake-case($s) { + $s.split(/>/)».lc.join: '-' +} - my subset HashMap of Pair where .value ~~ Associative; - # Builds CSS properties from an element from a tag name and attributes - multi method tag-style(Str $tag, *% where !.so) { - self.base-style($tag); - } - multi method tag-style($tag, *%attrs --> CSS::Properties:D) { - my CSS::Properties $css = self.base-style($tag).clone; - - for %attrs.keys.grep({%Layout{$_}:exists}) -> $key { - my $value := %attrs{$key}; - given %Layout{$key} { - when Str { - $css."$_"() = $value; - } - when HashMap { - $css."{.key}"() = $_ with .value{$value}; - } - when Code { - with .($key, $value) -> $kv { - $css."{$kv.key}"() = $_ with $kv.value; - } - } - default { - die "can't map attribute {$key} to {.raku}"; +# mapping of Tagged PDF attributes to CSS properties +our %Layout = %( + 'FontFamily'|'FontSize'|'FontStyle'|'FontWeight'|'FontVariant'|'FontStretch' + => -> Str $prop, $v { snake-case($prop) => $v ~ 'pt' }, + # IS0 32000-1 Table 343 – Standard layout attributes common to all standard structure types + 'Placement' => :display{ :Block, :Inline }, + 'WritingMode' => :direction{ :LrTb, :RlTb }, + 'BackgroundColor'|'BorderColor'|'Color' => -> $_, $c { + .&snake-case => '#' ~ $c.split(' ').map({sprintf("%02x", (.Num * 255).round)}).join; + }, + 'BorderStyle' => -> Str $prop, Str $s { + snake-case($prop) => [ $s.split(' ')>>.lc ]; + }, + 'BorderThickness'|'Padding' => -> Str $prop, Str $s { + snake-case($prop) => [ $s.split(' ').map: -> $pt { :$pt } ]; + }, + 'TextIdent'|'Width'|'Height'|'LineHeight' => -> Str $prop, Num() $pt { + # approximate + snake-case($prop) => :$pt; + }, + 'TextAlign' => -> Str $prop, Str $s { + snake-case($prop) => $s.lc; + }, + 'TextDecorationType' => -> Str $, Str $s { + text-decoration => $s.lc; + }, + # Custom properties which don't map well to CSS standard properties + 'SpaceBefore'|'SpaceAfter'|'StartIndent'|'EndIndent' => -> Str $prop, Num() $pt { + '-pdf-' ~ snake-case($prop) => :$pt; + }, + # Todo: BBox BlockAlign InlineAlign TBorderStyle TPadding TextDecorationColor TextDecorationThickness RubyAlign RubyPosition GlyphOrientationVertical +); + +my subset HashMap of Pair where .value ~~ Associative; +# Builds CSS properties from an element from a tag name and attributes +multi method tag-style(Str $tag, *% where !.so) { + self.base-style($tag); +} +multi method tag-style($tag, *%attrs --> CSS::Properties:D) { + my CSS::Properties $css = self.base-style($tag).clone; + + for %attrs.keys.grep({%Layout{$_}:exists}) -> $key { + my $value := %attrs{$key}; + given %Layout{$key} { + when Str { + $css."$_"() = $value; + } + when HashMap { + $css."{.key}"() = $_ with .value{$value}; + } + when Code { + with .($key, $value) -> $kv { + $css."{$kv.key}"() = $_ with $kv.value; } } + default { + die "can't map attribute {$key} to {.raku}"; + } } - - $css; } + $css; } =begin pod diff --git a/lib/CSS/TagSet/XHTML.rakumod b/lib/CSS/TagSet/XHTML.rakumod index afe587d..53fcb6e 100644 --- a/lib/CSS/TagSet/XHTML.rakumod +++ b/lib/CSS/TagSet/XHTML.rakumod @@ -1,162 +1,160 @@ -use v6; +unit class CSS::TagSet::XHTML; use CSS::TagSet :&load-css-tagset; +also does CSS::TagSet; -class CSS::TagSet::XHTML does CSS::TagSet { - use CSS::Module; - use CSS::Module::CSS3; - use CSS::Properties; - use URI; +use CSS::Module; +use CSS::Module::CSS3; +use CSS::Properties; +use URI; - has CSS::Properties %!props; - has SetHash %!link-pseudo; - has CSS::Module $.module = CSS::Module::CSS3.module; +has CSS::Properties %!props; +has SetHash %!link-pseudo; +has CSS::Module $.module = CSS::Module::CSS3.module; - constant %Tags is export(:Tags) = load-css-tagset(%?RESOURCES); +constant %Tags is export(:Tags) = load-css-tagset(%?RESOURCES); - method declarations { %Tags } +method declarations { %Tags } - method base-style(Str $prop) { - %!props{$prop} //= CSS::Properties.new(:$!module, declarations => %Tags{$prop} // []); - } - - # mapping of HTML attributes to CSS properties - constant %AttrProp = %( - align => '-xhtml-align', - background => 'background-image', - bgcolor => 'background-color', - border => 'border', - color => 'color', - colspan => '-xhtml-colspan', - dir => 'direction', - height => 'height', - rowspan => '-xhtml-rowspan', - ); +method base-style(Str $prop) { + %!props{$prop} //= CSS::Properties.new(:$!module, declarations => %Tags{$prop} // []); +} - # mapping of HTML attributes to containing tags - constant %AttrTags = %( - align => 'applet'|'caption'|'col'|'colgroup'|'hr'|'iframe'|'img'|'table'|'tbody'|'td'|'tfoot'|'th'|'thead'|'tr', - background => 'body'|'table'|'td'|'th', # obsolete in HTML5 - bgcolor => 'body'|'col'|'colgroup'|'marquee'|'table'|'tbody'|'tfoot'|'td'|'th'|'tr', # obsolete in HTML5 - border => 'img'|'object'|'table', # obsolete in HTML5 - color => 'basefont'|'font'|'hr', # obsolete in HTML5 - bdo => 'bidi-override', - dir => Str, # applicable to all - 'height'|'width' => 'canvas'|'embed'|'iframe'|'img'|'input'|'object'|'video', - 'colspan'|'rowspan' => 'td'|'th', +# mapping of HTML attributes to CSS properties +constant %AttrProp = %( + align => '-xhtml-align', + background => 'background-image', + bgcolor => 'background-color', + border => 'border', + color => 'color', + colspan => '-xhtml-colspan', + dir => 'direction', + height => 'height', + rowspan => '-xhtml-rowspan', +); + +# mapping of HTML attributes to containing tags +constant %AttrTags = %( + align => 'applet'|'caption'|'col'|'colgroup'|'hr'|'iframe'|'img'|'table'|'tbody'|'td'|'tfoot'|'th'|'thead'|'tr', + background => 'body'|'table'|'td'|'th', # obsolete in HTML5 + bgcolor => 'body'|'col'|'colgroup'|'marquee'|'table'|'tbody'|'tfoot'|'td'|'th'|'tr', # obsolete in HTML5 + border => 'img'|'object'|'table', # obsolete in HTML5 + color => 'basefont'|'font'|'hr', # obsolete in HTML5 + bdo => 'bidi-override', + dir => Str, # applicable to all + 'height'|'width' => 'canvas'|'embed'|'iframe'|'img'|'input'|'object'|'video', + 'colspan'|'rowspan' => 'td'|'th', +); + +method xpath-init($xpath-context) { + $xpath-context.registerFunction( + 'link-pseudo', + -> $name, $node-set { + my $elem = $node-set.first; + ? ($elem.tag ~~ 'a'|'link'|'area' && self.link-pseudo($name, $elem)); + }); +} +submethod TWEAK(:$xpath-context) { + my %CustomProps = %( + '-xhtml-align' => %( + :like, + ), + '-xhtml-colspan'|'-xhtml-rowspan' => %( + :synopsis, + :default(1), + :coerce(-> Int() $num { :$num }), + ), ); - method xpath-init($xpath-context) { - $xpath-context.registerFunction( - 'link-pseudo', - -> $name, $node-set { - my $elem = $node-set.first; - ? ($elem.tag ~~ 'a'|'link'|'area' && self.link-pseudo($name, $elem)); - }); - } - submethod TWEAK(:$xpath-context) { - my %CustomProps = %( - '-xhtml-align' => %( - :like, - ), - '-xhtml-colspan'|'-xhtml-rowspan' => %( - :synopsis, - :default(1), - :coerce(-> Int() $num { :$num }), - ), - ); - - for %CustomProps.pairs { - $!module.extend(:name(.key), |.value); - } + for %CustomProps.pairs { + $!module.extend(:name(.key), |.value); } +} - # any additional CSS styling based on HTML attributes - multi sub tweak-style('bdo', $css) { - $css.unicode-bidi //= :keyw; - } - multi sub tweak-style($, $,) is default { - } +# any additional CSS styling based on HTML attributes +multi sub tweak-style('bdo', $css) { + $css.unicode-bidi //= :keyw; +} +multi sub tweak-style($, $,) is default { +} - sub matching-media($media, $query) { - !$media.defined || !$query.defined || $query ~~ $media; - } +sub matching-media($media, $query) { + !$media.defined || !$query.defined || $query ~~ $media; +} - method !load-linked-stylesheet($e, URI:D :$base-url!, :$media, :$links) { - my @content; - - with $e.getAttribute('rel') { - when .lc eq 'stylesheet' { - with $e.getAttribute('href') -> URI() $_ { - if matching-media($media, $e.getAttribute('media')) { - if $links { - my URI $url = .rel2abs($base-url.directory); - my CSS::URI $uri .= new: :$url; - @content.push: $_ with $uri.get; - } - else { - warn "ignoring {$e.Str} - use :links option to enable"; - } +method !load-linked-stylesheet($e, URI:D :$base-url!, :$media, :$links) { + my @content; + + with $e.getAttribute('rel') { + when .lc eq 'stylesheet' { + with $e.getAttribute('href') -> URI() $_ { + if matching-media($media, $e.getAttribute('media')) { + if $links { + my URI $url = .rel2abs($base-url.directory); + my CSS::URI $uri .= new: :$url; + @content.push: $_ with $uri.get; + } + else { + warn "ignoring {$e.Str} - use :links option to enable"; } } } } - - @content; } - method stylesheet-content($doc, :$media, :$links) { - my URI() $base-url = $doc.?URI // './'; - my @content; - for $doc.findnodes('html/head/*') -> $e { - given $e.tag { - when 'style' { - @content.push: $e.textContent; - } - when 'link' { - @content.append: self!load-linked-stylesheet($e, :$base-url, :$media, :$links); - } + @content; +} + +method stylesheet-content($doc, :$media, :$links) { + my URI() $base-url = $doc.?URI // './'; + my @content; + for $doc.findnodes('html/head/*') -> $e { + given $e.tag { + when 'style' { + @content.push: $e.textContent; + } + when 'link' { + @content.append: self!load-linked-stylesheet($e, :$base-url, :$media, :$links); } } - @content; } + @content; +} - # Builds CSS properties from an element from a tag name and attributes - multi method tag-style(Str $tag, *% where !.so) { - self.base-style($tag); - } - multi method tag-style(Str $tag, :$hidden, *%attrs) { - my CSS::Properties $css = self.base-style($tag).clone; - $css.display = :keyw with $hidden; - - for %attrs.keys.grep({%AttrTags{$_}:exists && $tag ~~ %AttrTags{$_}}) { - my $name = %AttrProp{$_} // $_; - $css."$name"() = %attrs{$_}; - } - tweak-style($tag, $css); - $css; - } +# Builds CSS properties from an element from a tag name and attributes +multi method tag-style(Str $tag, *% where !.so) { + self.base-style($tag); +} +multi method tag-style(Str $tag, :$hidden, *%attrs) { + my CSS::Properties $css = self.base-style($tag).clone; + $css.display = :keyw with $hidden; - multi method link-pseudo(Str() $type, $node) is rw { - $.link-pseudo($type, $node.nodePath); + for %attrs.keys.grep({%AttrTags{$_}:exists && $tag ~~ %AttrTags{$_}}) { + my $name = %AttrProp{$_} // $_; + $css."$name"() = %attrs{$_}; } + tweak-style($tag, $css); + $css; +} - multi method link-pseudo('link', Str $path) is rw { - Proxy.new( - FETCH => { ! %!link-pseudo{$path} }, - STORE => { %!link-pseudo{$path}:delete }, - ); - } +multi method link-pseudo(Str() $type, $node) is rw { + $.link-pseudo($type, $node.nodePath); +} - multi method link-pseudo(Str $type, Str $path) is rw is default { - Proxy.new( - FETCH => { do with %!link-pseudo{$path} { .{$type.lc} } else { False } }, - STORE => -> $, Bool() $v { - (%!link-pseudo{$path} //= SetHash.new){$type.lc} = $v - }, - ); - } +multi method link-pseudo('link', Str $path) is rw { + Proxy.new( + FETCH => { ! %!link-pseudo{$path} }, + STORE => { %!link-pseudo{$path}:delete }, + ); +} +multi method link-pseudo(Str $type, Str $path) is rw is default { + Proxy.new( + FETCH => { do with %!link-pseudo{$path} { .{$type.lc} } else { False } }, + STORE => -> $, Bool() $v { + (%!link-pseudo{$path} //= SetHash.new){$type.lc} = $v + }, + ); } =begin pod