このウェブサイトでは、画像や引用をfigure
要素でマークアップしている。Markdownで書いているわけだが、もちろんそんな記法はないので、今までは生HTMLを書いていた。しかし、marked v0.4.0でのバグでハマりどうしようもなさそうだったため、markedの拡張機構を使って、なんとなくうまい感じになるようにする必要がでてきてしまった。
現在のHTML仕様ではp
要素が柔軟に使えるので、わざわざfigure
要素を使うべき、というわけではない。しかしMarkdownだとクラス名を自由に振れるわけではないので、普通の段落と画像だけの段落を区別できない。Selectors Level 4が自由に使えるようになるまでは、スタイル都合で画像や引用と普通の段落を明示的に区別したいという気持ちが強い。
figure
要素想定するパターンはキャプションのあるなしの2つだ。キャプションの記述には、Markdownのいくつかの記法にあるtitle
属性を流用するようにした(使ったことがない)。画像記法のそれだと実装が面倒くさかったため、リンクを張ることを前提に、ハイパーリンク記法のそれを利用する。
[![alt](src)](href "caption")
[![alt](src)](href)
このMarkdownから以下のHTMLを生成したい。
<figure>
<a href="href"><img alt="alt" src="src"></a>
<figcaption>caption</figcaption>
</figure>
<figure>
<a href="href"><img alt="alt" src="src"></a>
</figure>
実装は2つのフェーズに分けて行った。リンク側ではキャプションがあり、かつ内容が画像だったら、figcaption
要素でキャプションをマークアップして追加する。段落側では、リンクが張られた画像だけか、それに加えてfigcaption
要素があるなら、全体をp
要素ではなくfigure
要素でマークアップする。
const markupHyperlink = (href, title, content) => {
if (!title) {
return `<a href="${href}">${content}</a>`;
}
if (!content.startsWith("<img ")) {
return `<a href="${href}" title="${title}>${content}</a>`;
}
return `<a href="${href}">${content}</a>
<figcaption>${title}</figcaption>`;
};
const markupParagaraph = content => {
if (!/^<a\b.*?><img\b.*?><\/a>(\n<(figcaption)>.*?<\/\1>)?$/.exec(content)) {
return `<p>${content}</p>
`;
}
return `<figure>
${content}
</figure>
`;
};
figure
要素画像とは違い、引用元の表記があるかないかで分ける。流用出来そうな記法はないので、最終行が—
(EMダッシュ)で始まっていた場合は引用元とみなす、ということにする。そうでない場合はfigure
要素でマークアップしなくても良いので、何もしない。
> This is a quote.
>
> — <cite>[text](href)</cite>
> This is a quote.
— <cite>[text](href)</cite>
> This is a quote.
>
> This is a quote.
最初のみfigure
要素でマークアップし、以下のHTMLを生成したい。
<figure>
<blockquote>
<p>This is a quote.</p>
</blockquote>
<figcaption>— <cite><a href="href">text</a></cite></figcaption>
</figure>
<blockquote>
<p>This is a quote.</p>
</blockquote>
<p>— <cite><a href="href">text</a></cite></p>
<blockquote>
<p>This is a quote.</p>
<p>This is a quote.</p>
</blockquote>
実装は引用内の最後の行のみをチェックして行った。EMダッシュで始まっていないなら通常通り出力する。始まっていた場合は最後の行をp
要素からfigcaption
要素へ変換し、さらにfigure
要素で全体をマークアップする。
const markupQuote = content => {
const lines = content.trim().split("\n");
if (!lines[lines.length - 1].startsWith("<p>—")) {
return `<blockquote>
${lines.join("\n")}
</blockquote>
`;
}
lines.push(lines.pop().replace(/<(\/)?p>/g, "<$1figcaption>"));
return `<figure>
<blockquote>
${lines.join("\n")}
</blockquote>
</figure>
`;
};
もうちょっと特別なルールをなくしたい。画像の方はリンクを強要したくないし、引用と同じようにキャプションのあるなしで分けた方が良いかもしれない。EMダッシュはそこそこ書きやすいので、引用はこのような形でベストかもしれない。とにかくなんとなくで書いたら、スカッとしたHTMLになってほしい。
また、コードから正規表現をなくしたいところだが、それは難しそうだ。arr.push(arr.pop())
とやっているところもどうにかしたい。