Ghost Blog は MarkdownレンダリングにShowdownモジュールを利用しているが、何故かそれに含まれるtable拡張を利用していない。幾度か要望がでたり議論もあったりしたのだが、一向に進展しないためテーマレベルで代替策を講じることにしてみた。
jquery.ex-mark-table.js
このスクリプトはjQuery Extensionになっていて、ロードすると jQuery.markTable() メソッドが追加される。これはMarkdown形式のtable書式を含むオブジェクトを渡してやるとHTML-Tableにオンデマンドで整形する。
//
// jquery.ex-mark-table.js
//
// $Id: mark-table.js 143 2015-05-08 09:31:06Z askn $
//
(function ($) {
$.fn.markTable = function (config) {
var defaults = {
className: 'mark-table'
}
var options = $.extend(defaults, config);
return this.each( function (i) {
var title = $(this).attr("title");
var $exported = $("<table/>");
var $body = $("<tbody/>");
var table = [], align = [], header = [];
var imported = $(this).html().split(/([^\n]*\n)/);
while (imported.length) {
var line = imported.shift();
if (line) {
if (line.length == 0) continue;
var column = line.match(/([^\|]*\|)/g);
if (column && column.length > 0) {
column.shift();
table.push($.map(column, function (val, index) {
return val.replace(/\|$/, "");
}));
}
}
}
if (title != undefined) {
$("<caption/>").text(title).appendTo($exported);
}
if (table.length && table[0].length && !table[0][0].match(/(^\:-|-\:$)/)) {
header = table.shift();
}
if (table.length && table[0].length && table[0][0].match(/(^\:-|-\:$)/)) {
align = $.map(table.shift(), function (val, index) {
if (val.match(/^\:\-*\:$/)) {
return "center";
}
else if (val.match(/^\-*\:$/)) {
return "right";
}
else {
return "left";
}
});
}
if (header.length) {
var $head = $("<tr/>");
$.each(header, function (key, val) {
var $column = $("<th/>").html(val);
if (align[key]) {
$column.addClass(align[key]);
}
$head.append($column);
});
$("<thead/>").append($head).appendTo($exported);
}
if (table.length) {
while (table.length) {
var $line = $("<tr/>");
$.each(table.shift(), function (key, val) {
var $column = $("<td/>").html(val);
if (align[key]) {
$column.addClass(align[key]);
}
$line.append($column);
});
$line.appendTo($body);
}
$body.appendTo($exported);
}
$(this).replaceWith($("<div/>").addClass(options.className).append($exported));
});
};
})(jQuery);
// End of Script
これをさらに次のようにしてindex.js等から呼び出す。1
(function pageInit () {
$('code[class^="language-"]').each(function () {
var $this = $(this);
var attr = $this.attr('class').replace(/^language-?/, '');
var match = attr.match(/title:(.*)/);
if (match) {
$this.parent().attr('title', match[1]);
attr = attr.replace(/title:.*/, '');
}
if (attr != null && attr.match(/^table/)) {
// マークダウンテーブル
$this.parent().html($this.html()).markTable();
}
});
})();
Markdownの方では、ネイティブにMarkdownテーブル書式を書くことが出来ないので、全体をコード書式で括るようにする。このとき三連バッククォート2の直後にtableというキーワードを付与する。更にtaitle:キーワードを足すとここから行末までをテーブルキャプションにすることが出来る。3
```table title:テーブルキャプション
|foo |bar |baz |
|:---|:----:|----:|
|Left|center|right|
```
これを保存するとGhostは次のHTMLコードに変換する。
{gfm-js-extract-pre-1}
付与したキーワードにlanguage-前置詞が付いたclass名として埋め込まれるのがミソで、ようはこれをjQueryで検出してクライアントサイドでpreをまるごとtableセットに書き換えてやる。あとはCSSで表示を整形4してやればよい。
|foo |bar |baz |
|:---|:----:|----:|
|Left|center|right|
これの実際のHTMLはつぎのように展開されている。
<div class="mark-table">
<table>
<caption>テーブルキャプション</caption>
<thead>
<tr>
<th class="left">foo </th>
<th class="center">bar </th>
<th class="right">baz </th>
</tr>
</thead>
<tbody>
<tr>
<td class="left">Left</td>
<td class="center">center</td>
<td class="right">right</td>
</tr>
</tbody>
</table>
</div>
今後ネイティブにMarkdownテーブル記法が実装された場合は(本実装との互換性はともかく)三連バッククォートを除去するだけで対応できるはずだ。