246 lines
5.3 KiB
JavaScript
246 lines
5.3 KiB
JavaScript
/** Flow HTML to wrapped lines + link/form hit targets. */
|
|
|
|
var BARE_SUMMON_BLOCK = {
|
|
address: 1,
|
|
article: 1,
|
|
aside: 1,
|
|
blockquote: 1,
|
|
body: 1,
|
|
div: 1,
|
|
dl: 1,
|
|
dt: 1,
|
|
dd: 1,
|
|
fieldset: 1,
|
|
figcaption: 1,
|
|
figure: 1,
|
|
footer: 1,
|
|
form: 1,
|
|
h1: 1,
|
|
h2: 1,
|
|
h3: 1,
|
|
h4: 1,
|
|
h5: 1,
|
|
h6: 1,
|
|
header: 1,
|
|
hr: 1,
|
|
li: 1,
|
|
main: 1,
|
|
nav: 1,
|
|
ol: 1,
|
|
p: 1,
|
|
pre: 1,
|
|
section: 1,
|
|
table: 1,
|
|
tr: 1,
|
|
ul: 1
|
|
}
|
|
|
|
function bareSummonIsBlock(name, style) {
|
|
var d = bareSummonDisplay(style)
|
|
if (d === 'none') return false
|
|
if (d === 'inline') return false
|
|
if (d === 'block') return true
|
|
return !!BARE_SUMMON_BLOCK[name]
|
|
}
|
|
|
|
function bareSummonLayout(doc, opts) {
|
|
opts = opts || {}
|
|
var cols = Math.max(20, opts.cols || 80)
|
|
var sheets = opts.sheets || []
|
|
var numbers = opts.numbers !== false
|
|
var lines = []
|
|
var links = []
|
|
var forms = []
|
|
var cur = ''
|
|
var curMarks = []
|
|
|
|
function flush() {
|
|
if (cur === '' && !curMarks.length) return
|
|
lines.push({ text: cur, marks: curMarks })
|
|
cur = ''
|
|
curMarks = []
|
|
}
|
|
|
|
function emitNl() {
|
|
flush()
|
|
}
|
|
|
|
function emitText(s, mark) {
|
|
s = String(s || '')
|
|
if (!s) return
|
|
var i = 0
|
|
while (i < s.length) {
|
|
var ch = s.charAt(i)
|
|
if (ch === '\n') {
|
|
emitNl()
|
|
i++
|
|
continue
|
|
}
|
|
if (ch === '\r') {
|
|
i++
|
|
continue
|
|
}
|
|
if (cur.length >= cols) emitNl()
|
|
if (mark) {
|
|
curMarks.push({
|
|
col: cur.length,
|
|
ch: ch,
|
|
link: mark.link,
|
|
form: mark.form,
|
|
bold: mark.bold,
|
|
dim: mark.dim
|
|
})
|
|
}
|
|
cur += ch
|
|
i++
|
|
}
|
|
}
|
|
|
|
function emitSpace() {
|
|
if (cur && cur.charAt(cur.length - 1) !== ' ') emitText(' ')
|
|
}
|
|
|
|
function walk(node, mark) {
|
|
if (!node) return
|
|
if (node.type === 'text') {
|
|
var t = node.text || ''
|
|
if (!mark || !mark.pre) t = t.replace(/\s+/g, ' ')
|
|
if (t === ' ' || t === '') {
|
|
if (t === ' ') emitSpace()
|
|
return
|
|
}
|
|
emitText(t, mark)
|
|
return
|
|
}
|
|
if (node.type !== 'element') return
|
|
var st = bareSummonComputedStyle(node, sheets)
|
|
if (bareSummonDisplay(st) === 'none') return
|
|
var name = node.name
|
|
var nextMark = {
|
|
link: mark && mark.link,
|
|
form: mark && mark.form,
|
|
bold: mark && mark.bold,
|
|
dim: mark && mark.dim,
|
|
pre: mark && mark.pre
|
|
}
|
|
if (
|
|
st['font-weight'] === 'bold' ||
|
|
name === 'b' ||
|
|
name === 'strong' ||
|
|
/^h[1-6]$/.test(name)
|
|
)
|
|
nextMark.bold = true
|
|
if (name === 'pre' || name === 'code' || st['font-family'] === 'monospace')
|
|
nextMark.dim = true
|
|
if (name === 'pre' || st['white-space'] === 'pre') nextMark.pre = true
|
|
|
|
if (name === 'a' && node.attrs.href) {
|
|
var id = links.length
|
|
links.push({
|
|
id: id,
|
|
href: node.attrs.href,
|
|
text: bareSummonTextContent(node).replace(/\s+/g, ' ').trim()
|
|
})
|
|
nextMark.link = id
|
|
if (numbers) emitText('[' + (id + 1) + ']', nextMark)
|
|
}
|
|
if (name === 'br') {
|
|
emitNl()
|
|
return
|
|
}
|
|
if (name === 'hr') {
|
|
emitNl()
|
|
emitText('\u2500'.repeat(Math.min(cols, 40)))
|
|
emitNl()
|
|
return
|
|
}
|
|
if (name === 'img') {
|
|
var alt = node.attrs.alt || node.attrs.src || 'img'
|
|
emitText('[IMG ' + alt + ']')
|
|
return
|
|
}
|
|
if (
|
|
name === 'input' ||
|
|
name === 'textarea' ||
|
|
name === 'select' ||
|
|
name === 'button'
|
|
) {
|
|
var fid = forms.length
|
|
forms.push({
|
|
id: fid,
|
|
name: node.attrs.name || '',
|
|
type: (node.attrs.type || name).toLowerCase(),
|
|
value: node.attrs.value || '',
|
|
node: node
|
|
})
|
|
nextMark.form = fid
|
|
emitText('[' + (node.attrs.type || name) + ']')
|
|
if (name === 'input' && BARE_SUMMON_VOID[name]) return
|
|
}
|
|
if (name === 'iframe' || name === 'video' || name === 'audio') {
|
|
emitText(
|
|
'[' +
|
|
name.toUpperCase() +
|
|
(node.attrs.src ? ' ' + node.attrs.src : '') +
|
|
']'
|
|
)
|
|
return
|
|
}
|
|
|
|
var block = bareSummonIsBlock(name, st)
|
|
if (block) emitNl()
|
|
if (name === 'li') emitText('\u2022 ')
|
|
var ch = node.children || []
|
|
for (var i = 0; i < ch.length; i++) walk(ch[i], nextMark)
|
|
if (block) emitNl()
|
|
}
|
|
|
|
walk(doc.body, {})
|
|
flush()
|
|
while (lines.length && !String(lines[0].text || '').trim()) lines.shift()
|
|
while (lines.length && !String(lines[lines.length - 1].text || '').trim())
|
|
lines.pop()
|
|
|
|
var plain = lines.map(function (l) {
|
|
return l.text
|
|
})
|
|
return {
|
|
cols: cols,
|
|
lines: lines,
|
|
plain: plain,
|
|
links: links,
|
|
forms: forms,
|
|
title: doc.title || ''
|
|
}
|
|
}
|
|
|
|
function bareSummonReaderText(doc) {
|
|
var bits = []
|
|
function keep(n) {
|
|
return (
|
|
n &&
|
|
n.type === 'element' &&
|
|
(n.name === 'p' ||
|
|
n.name === 'h1' ||
|
|
n.name === 'h2' ||
|
|
n.name === 'h3' ||
|
|
n.name === 'li' ||
|
|
n.name === 'pre' ||
|
|
n.name === 'blockquote' ||
|
|
n.name === 'article')
|
|
)
|
|
}
|
|
function walk(n) {
|
|
if (!n) return
|
|
if (keep(n)) {
|
|
var t = bareSummonTextContent(n).replace(/\s+/g, ' ').trim()
|
|
if (t) bits.push(t)
|
|
return
|
|
}
|
|
var ch = n.children || []
|
|
for (var i = 0; i < ch.length; i++) walk(ch[i])
|
|
}
|
|
walk(doc.body)
|
|
return bits.join('\n\n')
|
|
}
|