Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 36 additions & 11 deletions scripts/table_filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,17 @@
// もとの markd を参考に拡張する
// https://github.com/markedjs/marked/blob/e5796ecc435a30f96939e6a7b2229c14264b4bf8/src/Renderer.js#L92
hexo.extend.filter.register('marked:renderer', function (renderer) {
// renderer は1ページの描画ごとに作り直されるので、直前の見出しと既出のラベルを
// ここに持てばページ単位で数えられる (#3231)
const heading = renderer.heading;
renderer.heading = function (text, level, ...rest) {
this._lastHeading = plainText(text);
return heading.call(this, text, level, ...rest);
};
renderer.table = function (header, body) {
return `<div class="scroll" tabindex="0" role="region" aria-label="${regionLabel(header)}">${table(header, body)}</div>\n`;
this._tableLabels ||= new Set();
const label = uniqueLabel(regionLabel(header), this._lastHeading, this._tableLabels);
return `<div class="scroll" tabindex="0" role="region" aria-label="${label}">${table(header, body)}</div>\n`;
};
});

Expand All @@ -17,6 +26,12 @@ const table = (header, body) => {
return '<table>\n' + '<thead>\n' + header + '</thead>\n' + body + '</table>';
};

const plainText = (html) =>
html
.replace(/<[^>]*>/g, '')
.replace(/\s+/g, ' ')
.trim();

/**
* 横スクロールする器の読み上げ名を、1行目の見出しから組む (#2961)。
*
Expand All @@ -33,18 +48,28 @@ const table = (header, body) => {
* 名前を列名から作るのは、1ページに複数の表があると「表」だけでは区別できないため。
*/
const regionLabel = (header) => {
const names = (header.match(/<th[^>]*>([\s\S]*?)<\/th>/g) || [])
.map((cell) =>
cell
.replace(/<[^>]*>/g, '')
.replace(/\s+/g, ' ')
.trim(),
)
.filter(Boolean);
const names = (header.match(/<th[^>]*>([\s\S]*?)<\/th>/g) || []).map(plainText).filter(Boolean);
if (!names.length) return '表';
// 長い見出しが並ぶ表もあるので頭を取る。
const joined = names.join(' / ').slice(0, 60);
return `表 ${joined}`;
};

/**
* 同じ列名の表が同じページに並ぶと名前が重複する (#3231。axe landmark-unique)。
* 2つ目以降は直前の見出しを先に置く。連番は順序しか言わないが、見出しは
* どの節の表かを言う。見出しが無いか、足しても重なるときだけ連番に落とす。
*/
const uniqueLabel = (base, heading, used) => {
const candidates = [base];
if (heading) candidates.push(`${heading.slice(0, 40)}の${base}`);
let label = candidates.find((c) => !used.has(c));
for (let n = 2; !label; n++) {
const numbered = `${candidates.at(-1)}(${n})`;
if (!used.has(numbered)) label = numbered;
}
used.add(label);
// **& は再エスケープしない。** header は marked が出した HTML で、本文の & は
// すでに &amp; になっている。もう一度潰すと &amp;amp; になって読み上げに出る
const joined = names.join(' / ').slice(0, 60);
return `表 ${joined}`.replace(/"/g, '&quot;').replace(/</g, '&lt;');
return label.replace(/"/g, '&quot;').replace(/</g, '&lt;');
};
Loading