/** 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')
}