import isstr from 'wsemi/src/isstr.mjs'
/**
* 將Camofox之accessibility snapshot轉換為簡易HTML
*
* 保留heading、paragraph、listitem、strong、emphasis、text、img等語意元素,跳過button、
* banner、navigation等結構容器標記
*
* @param {String} snapshot 輸入accessibility snapshot字串
* @param {String} [pageTitle=''] 輸入頁面標題字串,預設''
* @returns {String} 回傳簡易HTML字串
* @example
*
* import snapshotToHtml from './src/snapshotToHtml.mjs'
*
* console.log(snapshotToHtml('- heading "abc" [level=1]\n- paragraph: def', 'abc'))
* // => '<!DOCTYPE html><html><head><title>abc</title></head><body><article><h1>abc</h1>\n<h1>abc</h1>\n<p>def</p></article></body></html>'
*
*/
function snapshotToHtml(snapshot, pageTitle = '') {
//check
if (!isstr(snapshot)) {
snapshot = ''
}
if (!isstr(pageTitle)) {
pageTitle = ''
}
let esc = (s) => s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
let lines = snapshot.split('\n')
let htmlParts = []
for (let line of lines) {
let trimmed = line.replace(/^ *- */, '').trim()
if (!trimmed) {
continue
}
//heading "..." [level=N]
let headingMatch = trimmed.match(/^(?:')?heading\s+"(.+?)"\s*\[level=(\d)\]/)
if (headingMatch) {
let lvl = headingMatch[2]
htmlParts.push(`<h${lvl}>${esc(headingMatch[1])}</h${lvl}>`)
continue
}
//link與/url:皆跳過, 內容由子行處理
if (/^(?:')?link\s+"/.test(trimmed)) {
continue
}
if (/^\/url:\s+/.test(trimmed)) {
continue
}
//img "alt"
let imgMatch = trimmed.match(/^img\s+"(.+?)"/)
if (imgMatch) {
htmlParts.push(`<img alt="${esc(imgMatch[1])}">`)
continue
}
//paragraph與paragraph: text
let paraMatch = trimmed.match(/^paragraph(?::\s*(.+))?$/)
if (paraMatch) {
if (paraMatch[1]) {
htmlParts.push(`<p>${esc(paraMatch[1])}</p>`)
}
continue
}
//listitem與listitem: text
let liMatch = trimmed.match(/^listitem(?::\s*(.+))?$/)
if (liMatch) {
if (liMatch[1]) {
htmlParts.push(`<li>${esc(liMatch[1])}</li>`)
}
continue
}
//strong: text
let strongMatch = trimmed.match(/^strong:\s*"?(.+?)"?\s*$/)
if (strongMatch) {
htmlParts.push(`<strong>${esc(strongMatch[1])}</strong>`)
continue
}
//emphasis: text
let emMatch = trimmed.match(/^emphasis:\s*"?(.+?)"?\s*$/)
if (emMatch) {
htmlParts.push(`<em>${esc(emMatch[1])}</em>`)
continue
}
//text: "..."
let textMatch = trimmed.match(/^text:\s*"?(.+?)"?\s*$/)
if (textMatch) {
htmlParts.push(`<span>${esc(textMatch[1])}</span>`)
continue
}
//option "..." (微信附註)
let optionMatch = trimmed.match(/^(?:')?option\s+"(.+?)"/)
if (optionMatch) {
htmlParts.push(`<p>${esc(optionMatch[1])}</p>`)
continue
}
//結構與容器標記皆跳過
if (/^(button|banner|navigation|main|contentinfo|complementary|list)\b/.test(trimmed)) {
continue
}
//純文字行
let plainText = trimmed.replace(/\[e\d+\]/g, '').replace(/^['"]|['"]$/g, '').trim()
if (
plainText.length > 0 &&
!/^(link|img|heading|paragraph|listitem|strong|emphasis|text|option|button|banner|navigation|main|contentinfo|complementary|list)\b/.test(plainText)
) {
htmlParts.push(`<p>${esc(plainText)}</p>`)
}
}
let titleEsc = esc(pageTitle)
return (
'<!DOCTYPE html><html><head><title>' + titleEsc + '</title></head>' +
'<body><article><h1>' + titleEsc + '</h1>\n' +
htmlParts.join('\n') +
'</article></body></html>'
)
}
export default snapshotToHtml