Jekyll は Liquid および markdown 記法を使って各ページやレイアウトを記述するが、画像を Lightbox をつかってポップアップするには一手間加える必要がありました。
Jekyll での投稿での画像の扱い
ブログのページに写真やその他の画像を貼り付けるというのは至ってよくある状況なのですが、リンク先に画像がポンとあるだけでは正直寂しい。ページを動的生成するサイトならその辺はどうにでもなるかもしれませんが、Jekyll は静的ジェネレータ。必要なら先に生成しておく必要があるわけです。
まぁ、そういうサイトは Lightbox を使うことで簡単にデコレーションできる訳ですが…… jekyll は生の HTML で記述するわけじゃないので一手間加える必要があるわけですね。
Liquid 記法を使うのは HTML ページであり、あくまでも変数や繰り返しなどの制御構造を埋め込むためのものなので、Lightbox 自体は普通に link 要素で CSS を、script 要素で動作のためのスクリプトを埋め込んでやればいいだけです。 が……問題は markdown で記述する投稿内容の方です。
[![csh(tcsh)のみという意味?](/images/2019-10-02-tcsh_only_s.png)](/images/2019-10-02-tcsh_only.png)
markdown 記法でサムネイル付きの画像リンクを書くとこうなるわけですが、これをクリックしてもページ遷移して画像だけがポンと表示されるだけです。
<a href="/images/2019-10-02-tcsh_only.png" rel="lightbox" data-title="csh(tcsh)のみという意味?">![csh(tcsh)のみという意味?](/images/2019-10-02-tcsh_only_s.png)</a>
対して、LightBox を使用して画像をポップアップするように修正したバージョン。rel 属性よりは data-lightbox 属性を使う方が簡便ですね。 markdown 記法のままでは a タグに対して追加の属性を記述できないので、生の HTML でゴリゴリ書けばいいわけですが、これだと当然 Lightbox に依存してしまいますし、リンク要素であるという情報が markdown のパーサーに伝わらないため、状況によってはリンクそのものを無効化したいとか、後々 Lightbox 以外のテクノロジーを使って画像リンクを取り扱いたいときに手作業でやることが増えてしまいます。出来ればやりたくない。
つー事で。
markdown 記法の拡張ではなく、HTML 形式に変換された後に特定の書式の部分だけ a タグを書き換えるという対応で行こうかと思います。
Lightbox プラグイン
既存のプラグインとか探せばあるかもしれませんが、今回はドシンプルな決め打ちに近い方法でいいので自前でささっと実装してみました。
module Jekyll
class LightBox
ConfigName = 'lightbox'
GroupConfigName = 'lightbox_group'
def initialize(site, page)
@config = page[ConfigName] || site.config[ConfigName] || false
if @config
@config = case @config
when true; { ConfigName => Hash.new }
when Array; { ConfigName => config_value_normalize(@config) }
when Hash; @config
else
raise ArgumentError, "#{ConfigName} オプションの指定が間違っています。(enabled? => #{@config.inspect})"
end
end
case hash = page[GroupConfigName]
when false;
when nil;
when Hash;
hash.each {|key, value| @config[key] = config_value_normalize(value) }
else
raise ArgumentError, "#{GroupConfigName} オプションの指定が間違っています。(#{hash.inspect})"
end
end
def enable?; !!@config end
def replace(input)
find_lightbox_node(input)
replace_lightbox_node!(input)
input
end
private
def config_value_normalize(value)
case value
when Hash; value
when Array; value.to_h {|x| [x, x] }
when String; Hash.new(value => value)
else
raise ArgumentError, "lightbox オプションの指定が間違っています。(value => #{value.inspect})"
end
end
LightBoxMatcher = Regexp.new('<a href=\"([^\"]*?)\"><img src=\"(?:[^\"]*?)\" alt=\"([^\"]*?)\" />')
def find_lightbox_node(input)
input = input.to_s
lb = @config[ConfigName]
while md = LightBoxMatcher.match(input)
lb[md[1]] = md[2]
input = md.post_match
end
end
def replace_lightbox_node!(input)
@config.each do |group, hash|
hash.each do |url, alt|
input.gsub!("<a href=\"#{url}\">", "<a href=\"#{url}\" data-lightbox=\"#{group}\" data-title=\"#{alt}\">")
end
end
end
end
module LightBoxExtender
def lightbox_extend(input)
lightbox = LightBox.new(@context.registers[:site], @context.registers[:page])
lightbox.enable? ? lightbox.replace(input) : input
end
end
end
Liquid::Template.register_filter(Jekyll::LightBoxExtender)
Liquid 記法向けのフィルターで実装。レイアウトファイルなどで {{ content | lightbox_extend }}
と記述するだけで該当するリンク要素を Lightbox によるdecorationできます。他で使い回す可能性も鑑みて変換処理は別クラスでまとめました。
やってることは非常に単純。_config.yaml
および各投稿の Front Matter 部分に lightbox: true
と書けばリンク要素でその直下に画像要素がある場合に Lightbox に必要な属性を追加するようになっています。
自動で見つけてくれるのは画像要素を直下に持つリンク要素のみで、それ以外は lightbox: {"image_path.png" => "画像", "foobar.png" => "サンプル" }
等とすることで明示的に指定することもできます。リンク要素の中に画像要素がなくても明示的に指定していれば属性追加の処理を行います。
おまけ(Liquid 記法を投稿記事に埋め込む)
で、この記事を書いているときに困ったのが、このテキストの中に Liquid 記法を書いたらそれが展開されてしまったと言うこと。 いちおう記法としてエスケープする手段はあるらしいのですが。
うん、メンドイ(ぉぃ HTMLのエスケープ文字を使う方が楽かも……かと思ったけど、こっちはこっちで多重エスケープされてしまった…… orz