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