This commit is contained in:
daniel-j 2019-10-08 08:37:27 +02:00
parent 88b3d26f56
commit b9cfa96446
8 changed files with 159 additions and 159 deletions

View file

@ -136,7 +136,7 @@ class FimFic2Epub extends EventEmitter {
this.coverImage = null
this.coverFilename = ''
this.coverType = ''
this.coverImageDimensions = {width: 0, height: 0}
this.coverImageDimensions = { width: 0, height: 0 }
this.readingEase = null
this.hasRemoteResources = {
@ -246,7 +246,7 @@ class FimFic2Epub extends EventEmitter {
}
chapterContent = chapterContent.trim().replace(trimWhitespace, '')
const chapter = {content: chapterContent, notes: notesContent, notesFirst}
const chapter = { content: chapterContent, notes: notesContent, notesFirst }
ch.realWordCount = utils.htmlWordCount(chapter.content)
p = p.then(() => cleanMarkup(chapter.content).then((content) => {
@ -413,7 +413,7 @@ class FimFic2Epub extends EventEmitter {
notesFirst: chapter.notesFirst,
index: i
})).then((html) => {
this.findRemoteResources('ch_' + zeroFill(3, i + 1), {chapter: i}, html)
this.findRemoteResources('ch_' + zeroFill(3, i + 1), { chapter: i }, html)
if (this.options.kepubify) {
html = kepubify(html)
}
@ -426,7 +426,7 @@ class FimFic2Epub extends EventEmitter {
content: chapter.notes,
index: i
}, true)).then((html) => {
this.findRemoteResources('note_' + zeroFill(3, i + 1), {note: i}, html)
this.findRemoteResources('note_' + zeroFill(3, i + 1), { note: i }, html)
if (this.options.kepubify) {
html = kepubify(html)
}
@ -466,7 +466,7 @@ class FimFic2Epub extends EventEmitter {
this.zip = new JSZip()
this.zip.file('mimetype', 'application/epub+zip', {compression: 'STORE'})
this.zip.file('mimetype', 'application/epub+zip', { compression: 'STORE' })
this.zip.file('META-INF/container.xml', containerXml)
this.zip.file('OEBPS/content.opf', Buffer.from(await template.createOpf(this), 'utf8'))
@ -539,7 +539,7 @@ class FimFic2Epub extends EventEmitter {
type: isNode ? 'nodebuffer' : 'blob',
mimeType: 'application/epub+zip',
compression: 'DEFLATE',
compressionOptions: {level: 9}
compressionOptions: { level: 9 }
}, (metadata) => { // onUpdate
let currentPercent = Math.round(metadata.percent / 10) * 10
if (lastPercent !== currentPercent) {
@ -572,7 +572,7 @@ class FimFic2Epub extends EventEmitter {
streamFiles: false,
mimeType: 'application/epub+zip',
compression: 'DEFLATE',
compressionOptions: {level: 9}
compressionOptions: { level: 9 }
}, (metadata) => {
if (onUpdate) onUpdate(metadata)
let currentPercent = Math.round(metadata.percent / 20) * 20
@ -639,7 +639,7 @@ class FimFic2Epub extends EventEmitter {
filename = 'emoticon_' + emoticon[1]
}
remoteCounter++
this.remoteResources.set(cleanurl, {filename: filename, where: [where], originalUrl: url})
this.remoteResources.set(cleanurl, { filename: filename, where: [where], originalUrl: url })
}
}
@ -671,7 +671,7 @@ class FimFic2Epub extends EventEmitter {
return fontAwesomeCodes[name].charCodeAt(0)
})
let fontFile = require('font-awesome/fonts/fontawesome-webfont.ttf')
this.iconsFont = await subsetFont(fontFile, glyphs, {local: isNode})
this.iconsFont = await subsetFont(fontFile, glyphs, { local: isNode })
}
iconsStyle () {
@ -720,7 +720,7 @@ class FimFic2Epub extends EventEmitter {
let fontSize = 150
let width
do {
ctx.font = "bold " + fontSize + "px sans-serif"
ctx.font = 'bold ' + fontSize + 'px sans-serif'
width = ctx.measureText(title).width
fontSize -= 5
} while (width > canvas.width * 0.85)
@ -728,7 +728,7 @@ class FimFic2Epub extends EventEmitter {
ctx.fillText(title, canvas.width / 2, canvas.height * 0.2)
fontSize = 75
do {
ctx.font = fontSize + "px sans-serif"
ctx.font = fontSize + 'px sans-serif'
width = ctx.measureText(author).width
fontSize -= 5
} while (width > canvas.width * 0.7)
@ -869,7 +869,7 @@ class FimFic2Epub extends EventEmitter {
// remove leading and trailing <br /> tags and whitespace
chapter = chapter.replace(trimWhitespace, '')
return {content: chapter, notes: authorNotes, notesFirst: authorNotesPos < chapterPos}
return { content: chapter, notes: authorNotes, notesFirst: authorNotesPos < chapterPos }
}
replaceRemoteResources () {

View file

@ -17,7 +17,7 @@ export async function cleanMarkup (html) {
html = html.normalize('NFC') // normalize unicode
html = twemoji.parse(html, {ext: '.svg', folder: 'svg'})
html = twemoji.parse(html, { ext: '.svg', folder: 'svg' })
// replace HTML entities with decimal entities
html = html.replace(/\xA0/g, '&#160;')
@ -45,7 +45,7 @@ export async function cleanMarkup (html) {
// add alt attributes to images that don't have them
let imageEmbed = /<img src="(.*?)" \/>/g
html = await replaceAsync(html, imageEmbed, (match, src) => render(m('img', {src: entities.decode(src), alt: 'Image'}), {strict: true}))
html = await replaceAsync(html, imageEmbed, (match, src) => render(m('img', { src: entities.decode(src), alt: 'Image' }), { strict: true }))
// Fix links pointing to pages on fimfiction
// Example: <a href="/user/djazz" rel="nofollow">djazz</a>
@ -72,8 +72,8 @@ export async function cleanMarkup (html) {
let matchSoundCloud = /<p><a class="embed" href="(https:\/\/soundcloud\.com\/.*?)">.*?<\/a><\/p>/g
html = await replaceAsync(html, matchSoundCloud, (match, url) => {
return render(m('.soundcloud.leftalign', [
'SoundCloud: ', m('a', {href: entities.decode(url), rel: 'nofollow'}, url.replace('https://soundcloud.com/', '').replace(/[-_]/g, ' ').replace('/', ' - ').replace(/ {2}/g, ' '))
]), {strict: true})
'SoundCloud: ', m('a', { href: entities.decode(url), rel: 'nofollow' }, url.replace('https://soundcloud.com/', '').replace(/[-_]/g, ' ').replace('/', ' - ').replace(/ {2}/g, ' '))
]), { strict: true })
})
if (cache.size === 0) {
@ -119,11 +119,11 @@ export async function cleanMarkup (html) {
return Promise.resolve(match)
}
return render(m('figure.youtube', [
m('a', {href: youtubeUrl, rel: 'nofollow'},
m('img', {src: thumbnail, alt: title})
m('a', { href: youtubeUrl, rel: 'nofollow' },
m('img', { src: thumbnail, alt: title })
),
m('figcaption', m('a', {href: youtubeUrl, rel: 'nofollow'}, caption))
]), {strict: true})
m('figcaption', m('a', { href: youtubeUrl, rel: 'nofollow' }, caption))
]), { strict: true })
}
}

View file

@ -20,11 +20,11 @@ function safariHandler (ev) {
delete safariQueue[url]
if (responseType === 'blob') {
let blob = new Blob([data], {type: type})
let blob = new Blob([data], { type: type })
cb(blob, type)
} else {
if (!responseType) {
let blob = new Blob([data], {type: type})
let blob = new Blob([data], { type: type })
let fr = new FileReader()
fr.onloadend = function () {
cb(fr.result, type)
@ -49,7 +49,7 @@ function fetchBackground (url, responseType) {
}))
})
} else if (typeof safari !== 'undefined') {
safariQueue[url] = {cb: resolve, responseType: responseType}
safariQueue[url] = { cb: resolve, responseType: responseType }
safari.self.tab.dispatchMessage('remote', url)
} else {
resolve(null)

View file

@ -7,7 +7,7 @@ export default function kepubify (html) {
const tree = et.parse(html)
const body = tree.find('./body')
addDivs(body)
const state = {paragraph: 0, segment: 0}
const state = { paragraph: 0, segment: 0 }
body.getchildren().forEach((child) => {
fixupTree(child, body)
addSpansToNode(child, body, state)
@ -21,8 +21,8 @@ const specialTags = /^(img|pre|svg)$/i
const paragraphTags = /^(p|ol|ul)$/i
function addDivs (body) {
const bookInner = et.Element('div', {class: 'book-inner'})
const bookColumns = et.SubElement(bookInner, 'div', {class: 'book-columns'})
const bookInner = et.Element('div', { class: 'book-inner' })
const bookColumns = et.SubElement(bookInner, 'div', { class: 'book-columns' })
bookColumns._children = body.getchildren()
body._children = [bookInner]

View file

@ -79,7 +79,7 @@ dialogContainer.id = 'epubDialogContainer'
document.body.appendChild(dialogContainer)
let checkbox = {
view: function ({attrs, children}) {
view: function ({ attrs, children }) {
return m('label.toggleable-switch', [
m('input', Object.assign({
type: 'checkbox'
@ -196,7 +196,7 @@ let dialog = {
this.ondown = (e) => {
let rect = this.el().firstChild.getBoundingClientRect()
let offset = {x: e.pageX - rect.left - document.documentElement.scrollLeft, y: e.pageY - rect.top - document.documentElement.scrollTop}
let offset = { x: e.pageX - rect.left - document.documentElement.scrollLeft, y: e.pageY - rect.top - document.documentElement.scrollTop }
this.dragging(true)
let onmove = (e) => {
e.preventDefault()
@ -238,62 +238,62 @@ let dialog = {
view (vnode) {
let ctrl = vnode.state
return m('.drop-down-pop-up-container', {oncreate: ctrl.onOpen.bind(ctrl)}, m('.drop-down-pop-up', {style: {'min-width': '720px'}}, [
m('h1', {onmousedown: ctrl.ondown}, m('i.fa.fa-book'), 'Export to EPUB (v' + FIMFIC2EPUB_VERSION + ')', m('a.close_button', {onclick: closeDialog})),
return m('.drop-down-pop-up-container', { oncreate: ctrl.onOpen.bind(ctrl) }, m('.drop-down-pop-up', { style: { 'min-width': '720px' } }, [
m('h1', { onmousedown: ctrl.ondown }, m('i.fa.fa-book'), 'Export to EPUB (v' + FIMFIC2EPUB_VERSION + ')', m('a.close_button', { onclick: closeDialog })),
m('.drop-down-pop-up-content', [
ctrl.isLoading() ? m('div', {style: 'text-align:center;'}, m('i.fa.fa-spin.fa-spinner', {style: 'font-size:50px; margin:20px; color:#777;'})) : m('table.properties', [
m('tr', m('td.section_header', {colspan: 3}, m('b', 'General settings'))),
m('tr', m('td.label', 'Title'), m('td', {colspan: 2}, m('input', {type: 'text', value: ctrl.title(), onchange: m.withAttr('value', ctrl.title)}))),
m('tr', m('td.label', 'Author'), m('td', {colspan: 2}, m('input', {type: 'text', value: ctrl.author(), onchange: m.withAttr('value', ctrl.author)}))),
ctrl.isLoading() ? m('div', { style: 'text-align:center;' }, m('i.fa.fa-spin.fa-spinner', { style: 'font-size:50px; margin:20px; color:#777;' })) : m('table.properties', [
m('tr', m('td.section_header', { colspan: 3 }, m('b', 'General settings'))),
m('tr', m('td.label', 'Title'), m('td', { colspan: 2 }, m('input', { type: 'text', value: ctrl.title(), onchange: m.withAttr('value', ctrl.title) }))),
m('tr', m('td.label', 'Author'), m('td', { colspan: 2 }, m('input', { type: 'text', value: ctrl.author(), onchange: m.withAttr('value', ctrl.author) }))),
m('tr', m('td.label', 'Custom cover image'),
m('td',
ctrl.checkboxCoverUrl() ? m('input', {type: 'url', placeholder: 'Image URL', onchange: m.withAttr('value', ctrl.coverUrl)}) : m('input', {type: 'file', accept: 'image/*', onchange: ctrl.setCoverFile, onupdate: ctrl.setCoverFile})
ctrl.checkboxCoverUrl() ? m('input', { type: 'url', placeholder: 'Image URL', onchange: m.withAttr('value', ctrl.coverUrl) }) : m('input', { type: 'file', accept: 'image/*', onchange: ctrl.setCoverFile, onupdate: ctrl.setCoverFile })
),
m('td', {style: 'width: 1px'}, m(checkbox, {checked: ctrl.checkboxCoverUrl(), onchange: m.withAttr('checked', ctrl.checkboxCoverUrl)}, 'Use image URL'))
m('td', { style: 'width: 1px' }, m(checkbox, { checked: ctrl.checkboxCoverUrl(), onchange: m.withAttr('checked', ctrl.checkboxCoverUrl) }, 'Use image URL'))
),
m('tr', m('td.label', 'Paragraph style'), m('td', {colspan: 2},
m('select', {onchange: m.withAttr('value', ctrl.paragraphStyle)}, selectOptions([
m('tr', m('td.label', 'Paragraph style'), m('td', { colspan: 2 },
m('select', { onchange: m.withAttr('value', ctrl.paragraphStyle) }, selectOptions([
['indented', 'Indent first line in all paragraphs except the first (Traditional Paperback)'],
['spaced', 'Separate each paragraph with double space (Traditional Web)'],
['both', 'Double space and indent all paragraphs except first (Fusion)'],
['indentedall', 'Indent all paragraphs including the first (Modified Traditional)']
], ctrl.paragraphStyle()))
)),
m('tr', m('td.label', {style: 'vertical-align: top;'}, 'Options'), m('td', {colspan: 2},
m(checkbox, {checked: ctrl.typogrify(), onchange: m.withAttr('checked', ctrl.typogrify)}, 'Apply typographic fixes (smart quotes, dashes etc.)'),
m(checkbox, {checked: ctrl.showChapterHeadings(), onchange: m.withAttr('checked', ctrl.showChapterHeadings)}, 'Add chapter headings'),
m(checkbox, {checked: ctrl.showChapterWordCount(), onchange: m.withAttr('checked', ctrl.showChapterWordCount), disabled: !ctrl.showChapterHeadings()}, 'Include word count in chapter heading'),
m(checkbox, {checked: ctrl.showChapterDuration(), onchange: m.withAttr('checked', ctrl.showChapterDuration), disabled: !ctrl.showChapterHeadings()}, 'Include time to read in chapter heading'),
m(checkbox, {checked: ctrl.addCommentsLink(), onchange: m.withAttr('checked', ctrl.addCommentsLink)}, 'Add link to online comments (at the end of chapters)'),
m(checkbox, {checked: ctrl.includeAuthorNotes(), onchange: m.withAttr('checked', ctrl.includeAuthorNotes)}, 'Include author\'s notes'),
m(checkbox, {checked: ctrl.useAuthorNotesIndex(), onchange: m.withAttr('checked', ctrl.useAuthorNotesIndex), disabled: !ctrl.includeAuthorNotes()}, 'Put all notes at the end of the ebook'),
m(checkbox, {checked: ctrl.calculateReadingEase(), onchange: m.withAttr('checked', ctrl.calculateReadingEase)}, 'Calculate Flesch reading ease'),
m(checkbox, {checked: ctrl.addChapterBars(), onchange: m.withAttr('checked', ctrl.addChapterBars)}, 'Show reading progress and chapter lengths as bars'),
m(checkbox, {checked: ctrl.includeExternal(), onchange: m.withAttr('checked', ctrl.includeExternal)}, 'Download & include remote content (embed images)'),
m('div', {style: 'font-size: 0.9em; line-height: 1em; margin-top: 4px; margin-bottom: 6px; opacity: 0.6;'}, 'Note: Disabling this creates invalid EPUBs and requires internet access to see remote content. Only cover image will be embedded.'),
m(checkbox, {checked: ctrl.kepubify(), onchange: m.withAttr('checked', ctrl.kepubify)}, 'Export as Kobo EPUB, this adds some Kobo-specific div/span tags.')
m('tr', m('td.label', { style: 'vertical-align: top;' }, 'Options'), m('td', { colspan: 2 },
m(checkbox, { checked: ctrl.typogrify(), onchange: m.withAttr('checked', ctrl.typogrify) }, 'Apply typographic fixes (smart quotes, dashes etc.)'),
m(checkbox, { checked: ctrl.showChapterHeadings(), onchange: m.withAttr('checked', ctrl.showChapterHeadings) }, 'Add chapter headings'),
m(checkbox, { checked: ctrl.showChapterWordCount(), onchange: m.withAttr('checked', ctrl.showChapterWordCount), disabled: !ctrl.showChapterHeadings() }, 'Include word count in chapter heading'),
m(checkbox, { checked: ctrl.showChapterDuration(), onchange: m.withAttr('checked', ctrl.showChapterDuration), disabled: !ctrl.showChapterHeadings() }, 'Include time to read in chapter heading'),
m(checkbox, { checked: ctrl.addCommentsLink(), onchange: m.withAttr('checked', ctrl.addCommentsLink) }, 'Add link to online comments (at the end of chapters)'),
m(checkbox, { checked: ctrl.includeAuthorNotes(), onchange: m.withAttr('checked', ctrl.includeAuthorNotes) }, 'Include author\'s notes'),
m(checkbox, { checked: ctrl.useAuthorNotesIndex(), onchange: m.withAttr('checked', ctrl.useAuthorNotesIndex), disabled: !ctrl.includeAuthorNotes() }, 'Put all notes at the end of the ebook'),
m(checkbox, { checked: ctrl.calculateReadingEase(), onchange: m.withAttr('checked', ctrl.calculateReadingEase) }, 'Calculate Flesch reading ease'),
m(checkbox, { checked: ctrl.addChapterBars(), onchange: m.withAttr('checked', ctrl.addChapterBars) }, 'Show reading progress and chapter lengths as bars'),
m(checkbox, { checked: ctrl.includeExternal(), onchange: m.withAttr('checked', ctrl.includeExternal) }, 'Download & include remote content (embed images)'),
m('div', { style: 'font-size: 0.9em; line-height: 1em; margin-top: 4px; margin-bottom: 6px; opacity: 0.6;' }, 'Note: Disabling this creates invalid EPUBs and requires internet access to see remote content. Only cover image will be embedded.'),
m(checkbox, { checked: ctrl.kepubify(), onchange: m.withAttr('checked', ctrl.kepubify) }, 'Export as Kobo EPUB, this adds some Kobo-specific div/span tags.')
)),
m('tr', m('td.label', 'Words per minute'), m('td', {colspan: 2},
m('input', {type: 'number', min: 0, step: 1, value: ctrl.wordsPerMinute(), onchange: m.withAttr('value', ctrl.wordsPerMinute), placeholder: '200 (default)', style: {width: '140px', float: 'left', marginRight: '.75rem', marginTop: '.35rem', position: 'relative', zIndex: 1}}),
m('div', {style: 'font-size: 0.9em; line-height: 1em; margin-top: 4px; margin-bottom: 6px; opacity: 0.6;'}, 'This is used to estimate the time it takes to read the story. Take a test to find out your reading speed.', m('br'), 'Set to 0 to disable.')
m('tr', m('td.label', 'Words per minute'), m('td', { colspan: 2 },
m('input', { type: 'number', min: 0, step: 1, value: ctrl.wordsPerMinute(), onchange: m.withAttr('value', ctrl.wordsPerMinute), placeholder: '200 (default)', style: { width: '140px', float: 'left', marginRight: '.75rem', marginTop: '.35rem', position: 'relative', zIndex: 1 } }),
m('div', { style: 'font-size: 0.9em; line-height: 1em; margin-top: 4px; margin-bottom: 6px; opacity: 0.6;' }, 'This is used to estimate the time it takes to read the story. Take a test to find out your reading speed.', m('br'), 'Set to 0 to disable.')
)),
m('tr', m('td.section_header', {colspan: 3}, m('b', 'Metadata customization'))),
m('tr', m('td.label', {style: 'vertical-align: top;'}, 'Description'), m('td', {colspan: 2}, m('textarea', {oncreate: ({dom}) => autosize(dom), onchange: ctrl.setDescription}, ctrl.description()))),
m('tr', m('td.label', {style: 'vertical-align: top;'}, 'Categories'), m('td', {colspan: 2},
m('textarea', {rows: 2, oncreate: ({dom}) => autosize(dom), onchange: ctrl.setSubjects}, ctrl.subjects().join('\n')),
m(checkbox, {checked: ctrl.joinSubjects(), onchange: m.withAttr('checked', ctrl.joinSubjects)}, 'Join categories and separate with commas')
m('tr', m('td.section_header', { colspan: 3 }, m('b', 'Metadata customization'))),
m('tr', m('td.label', { style: 'vertical-align: top;' }, 'Description'), m('td', { colspan: 2 }, m('textarea', { oncreate: ({ dom }) => autosize(dom), onchange: ctrl.setDescription }, ctrl.description()))),
m('tr', m('td.label', { style: 'vertical-align: top;' }, 'Categories'), m('td', { colspan: 2 },
m('textarea', { rows: 2, oncreate: ({ dom }) => autosize(dom), onchange: ctrl.setSubjects }, ctrl.subjects().join('\n')),
m(checkbox, { checked: ctrl.joinSubjects(), onchange: m.withAttr('checked', ctrl.joinSubjects) }, 'Join categories and separate with commas')
))
]),
m('.drop-down-pop-up-footer', [
m('button.styled_button', {onclick: ctrl.createEpub, disabled: ffcProgress() >= 0 && ffcProgress() < 1, style: 'float: right'}, 'Download ' + (ctrl.kepubify() ? 'Kobo EPUB' : 'EPUB')),
m('button.styled_button', { onclick: ctrl.createEpub, disabled: ffcProgress() >= 0 && ffcProgress() < 1, style: 'float: right' }, 'Download ' + (ctrl.kepubify() ? 'Kobo EPUB' : 'EPUB')),
ffcProgress() >= 0 ? m('.rating_container',
m('.rating-bar', {style: {background: 'rgba(0, 0, 0, 0.2)', 'margin-right': '5px'}}, m('.like-bar', {style: {width: Math.max(0, ffcProgress()) * 100 + '%'}})),
m('.rating-bar', { style: { background: 'rgba(0, 0, 0, 0.2)', 'margin-right': '5px' } }, m('.like-bar', { style: { width: Math.max(0, ffcProgress()) * 100 + '%' } })),
' ',
ffcProgress() >= 0 && ffcProgress() < 1 ? [ m('i.fa.fa-spin.fa-spinner'), m.trust('&nbsp;&nbsp;') ] : null,
ffcStatus()
) : null,
m('div', {style: 'clear: both'})
m('div', { style: 'clear: both' })
])
])
]))
@ -351,7 +351,7 @@ function createEpub (model) {
model.wordsPerMinute(ffc.options.wordsPerMinute)
redraw()
chrome.storage.sync.set({ffcOptions: ffc.options, version: FIMFIC2EPUB_VERSION})
chrome.storage.sync.set({ ffcOptions: ffc.options, version: FIMFIC2EPUB_VERSION })
chain
.then(ffc.fetchAll.bind(ffc))
@ -402,7 +402,7 @@ function onProgress (percent, status) {
}
if (pageStoryId && isChromeExt) {
chrome.runtime.sendMessage({showPageAction: true})
chrome.runtime.sendMessage({ showPageAction: true })
chrome.runtime.onMessage.addListener(function (request) {
if (request === 'pageAction') {
openStory(pageStoryId)

View file

@ -47,7 +47,7 @@ function chapterBars (chapters, currentChapter = -1, highlightCurrent = false) {
const rows = Math.floor(wordCounts.length / barCount) + 1
const rowHeight = 30 + rowSpacing
return m('svg.chapterbars', {
style: {height: rows * 3 + 'em'},
style: { height: rows * 3 + 'em' },
viewBox: '0 0 ' + barCount * (barWidth + barSpacing) + ' ' + rowHeight * rows,
xmlns: NS.SVG,
fill: 'currentColor'
@ -61,16 +61,16 @@ function chapterBars (chapters, currentChapter = -1, highlightCurrent = false) {
} else if (i > currentChapter) {
opacity = 0.35
}
return m('rect', {x: x * (barWidth + barSpacing), width: barWidth, y: y * rowHeight + (rowHeight - rowSpacing - height), height, opacity})
return m('rect', { x: x * (barWidth + barSpacing), width: barWidth, y: y * rowHeight + (rowHeight - rowSpacing - height), height, opacity })
}))
}
export function createChapter (ffc, ch, isNotesChapter) {
let {content, notes, notesFirst, title, link, linkNotes, index, showHeadings, showDuration, showWordCount} = ch
let { content, notes, notesFirst, title, link, linkNotes, index, showHeadings, showDuration, showWordCount } = ch
let sections = [
m.trust(content || ''),
notes ? m('div#author_notes', {className: notesFirst ? 'top' : 'bottom'}, [
notes ? m('div#author_notes', { className: notesFirst ? 'top' : 'bottom' }, [
m('p', m('b', 'Author\'s Note:')),
m.trust(notes)]) : null
]
@ -84,13 +84,13 @@ export function createChapter (ffc, ch, isNotesChapter) {
return Promise.all([
render(
m('html', {xmlns: NS.XHTML, 'xmlns:epub': NS.OPS, lang: 'en'}, [
m('html', { xmlns: NS.XHTML, 'xmlns:epub': NS.OPS, lang: 'en' }, [
m('head', [
m('meta', {charset: 'utf-8'}),
m('link', {rel: 'stylesheet', type: 'text/css', href: '../Styles/style.css'}),
m('meta', { charset: 'utf-8' }),
m('link', { rel: 'stylesheet', type: 'text/css', href: '../Styles/style.css' }),
m('title', title)
]),
m('body', {'epub:type': 'bodymatter chapter'}, m('div', [
m('body', { 'epub:type': 'bodymatter chapter' }, m('div', [
showHeadings ? m('.chapter-title', [
!isNotesChapter && (showDuration || showWordCount) ? m('aside.info',
showDuration ? m('span.label', ffc.options.wordsPerMinute ? calcReadingTime(ffc, ffc.storyInfo.chapters[index].realWordCount) : '') : null,
@ -100,15 +100,15 @@ export function createChapter (ffc, ch, isNotesChapter) {
m('hr.old')
]) : null,
tokenContent,
(link || linkNotes || isNotesChapter) ? m('p.double', {style: 'text-align: center; clear: both;'},
link ? m('a.chaptercomments', {href: link + '#comment_list'}, 'Read chapter comments online') : null,
linkNotes ? m('a.chaptercomments', {href: linkNotes}, 'Read author\'s note') : null,
isNotesChapter ? m('a.chaptercomments', {href: './chapter_' + zeroFill(3, index + 1) + '.xhtml'}, 'Read chapter') : null
(link || linkNotes || isNotesChapter) ? m('p.double', { style: 'text-align: center; clear: both;' },
link ? m('a.chaptercomments', { href: link + '#comment_list' }, 'Read chapter comments online') : null,
linkNotes ? m('a.chaptercomments', { href: linkNotes }, 'Read author\'s note') : null,
isNotesChapter ? m('a.chaptercomments', { href: './chapter_' + zeroFill(3, index + 1) + '.xhtml' }, 'Read chapter') : null
) : null,
!isNotesChapter && ffc.options.addChapterBars ? chapterBars(ffc.storyInfo.chapters, index) : null
]))
])
, {strict: true}),
, { strict: true }),
render(sections)
]).then(([chapterPage, sectionsData]) => {
chapterPage = '<?xml version="1.0" encoding="utf-8"?>\n<!DOCTYPE html>\n' + chapterPage
@ -170,23 +170,23 @@ export function createOpf (ffc) {
// only add each file once
if (remoteCache.has(r.dest)) return
remoteCache.add(r.dest)
remotes.push(m('item', {id: r.filename, href: r.dest, 'media-type': r.type}))
remotes.push(m('item', { id: r.filename, href: r.dest, 'media-type': r.type }))
})
let manifestChapters = ffc.storyInfo.chapters.map((ch, num) =>
m('item', {id: 'chapter_' + zeroFill(3, num + 1), href: 'Text/chapter_' + zeroFill(3, num + 1) + '.xhtml', 'media-type': 'application/xhtml+xml', properties: ((ch.remote ? 'remote-resources' : '') + (ffc.options.addChapterBars ? ' svg' : '')).trim() || null})
m('item', { id: 'chapter_' + zeroFill(3, num + 1), href: 'Text/chapter_' + zeroFill(3, num + 1) + '.xhtml', 'media-type': 'application/xhtml+xml', properties: ((ch.remote ? 'remote-resources' : '') + (ffc.options.addChapterBars ? ' svg' : '')).trim() || null })
)
let spineChapters = ffc.storyInfo.chapters.map((ch, num) =>
m('itemref', {idref: 'chapter_' + zeroFill(3, num + 1)})
m('itemref', { idref: 'chapter_' + zeroFill(3, num + 1) })
)
let manifestNotes = []
let spineNotes = []
if (ffc.options.includeAuthorNotes && ffc.options.useAuthorNotesIndex && ffc.hasAuthorNotes) {
spineNotes.push(m('itemref', {idref: 'notesnav'}))
spineNotes.push(m('itemref', { idref: 'notesnav' }))
ffc.chaptersWithNotes.forEach((num) => {
let id = 'note_' + zeroFill(3, num + 1)
manifestNotes.push(m('item', {id: id, href: 'Text/' + id + '.xhtml', 'media-type': 'application/xhtml+xml'}))
spineNotes.push(m('itemref', {idref: id}))
manifestNotes.push(m('item', { id: id, href: 'Text/' + id + '.xhtml', 'media-type': 'application/xhtml+xml' }))
spineNotes.push(m('itemref', { idref: id }))
})
}
@ -196,55 +196,55 @@ export function createOpf (ffc) {
}
return render(
m('package', {xmlns: NS.OPF, version: '3.0', 'unique-identifier': 'BookId'}, [
m('metadata', {'xmlns:dc': NS.DC, 'xmlns:opf': NS.OPF}, [
m('package', { xmlns: NS.OPF, version: '3.0', 'unique-identifier': 'BookId' }, [
m('metadata', { 'xmlns:dc': NS.DC, 'xmlns:opf': NS.OPF }, [
m('dc:identifier#BookId', ffc.storyInfo.uuid),
m('dc:title', ffc.storyInfo.title),
m('dc:creator#cre', ffc.storyInfo.author.name),
m('meta', {refines: '#cre', property: 'role', scheme: 'marc:relators'}, 'aut'),
m('meta', { refines: '#cre', property: 'role', scheme: 'marc:relators' }, 'aut'),
m('dc:date', new Date((ffc.storyInfo.publishDate || ffc.storyInfo.date_modified) * 1000).toISOString().substring(0, 10)),
m('dc:publisher', 'Fimfiction'),
ffc.storyInfo.short_description ? m('dc:description', ffc.storyInfo.short_description) : null,
m('dc:source', ffc.storyInfo.url),
m('dc:language', 'en'),
ffc.coverImage ? m('meta', {name: 'cover', content: 'cover'}) : null,
m('meta', {property: 'dcterms:modified'}, new Date(ffc.storyInfo.date_modified * 1000).toISOString().replace('.000', ''))
ffc.coverImage ? m('meta', { name: 'cover', content: 'cover' }) : null,
m('meta', { property: 'dcterms:modified' }, new Date(ffc.storyInfo.date_modified * 1000).toISOString().replace('.000', ''))
].concat(subjects.map((s) =>
m('dc:subject', s)
), m('meta', {name: 'fimfic2epub version', content: FIMFIC2EPUB_VERSION}))),
), m('meta', { name: 'fimfic2epub version', content: FIMFIC2EPUB_VERSION }))),
m('manifest', [
ffc.coverImage ? m('item', {id: 'cover', href: ffc.coverFilename, 'media-type': ffc.coverType, properties: 'cover-image'}) : null,
m('item', {id: 'ncx', href: 'toc.ncx', 'media-type': 'application/x-dtbncx+xml'}),
m('item', {id: 'nav', 'href': 'nav.xhtml', 'media-type': 'application/xhtml+xml', properties: 'nav' + (ffc.options.addChapterBars && ffc.storyInfo.chapters.length > 1 ? ' svg' : '')}),
ffc.options.includeAuthorNotes && ffc.options.useAuthorNotesIndex && ffc.hasAuthorNotes ? m('item', {id: 'notesnav', 'href': 'notesnav.xhtml', 'media-type': 'application/xhtml+xml'}) : null,
ffc.coverImage ? m('item', { id: 'cover', href: ffc.coverFilename, 'media-type': ffc.coverType, properties: 'cover-image' }) : null,
m('item', { id: 'ncx', href: 'toc.ncx', 'media-type': 'application/x-dtbncx+xml' }),
m('item', { id: 'nav', 'href': 'nav.xhtml', 'media-type': 'application/xhtml+xml', properties: 'nav' + (ffc.options.addChapterBars && ffc.storyInfo.chapters.length > 1 ? ' svg' : '') }),
ffc.options.includeAuthorNotes && ffc.options.useAuthorNotesIndex && ffc.hasAuthorNotes ? m('item', { id: 'notesnav', 'href': 'notesnav.xhtml', 'media-type': 'application/xhtml+xml' }) : null,
m('item', {id: 'style', href: 'Styles/style.css', 'media-type': 'text/css'}),
m('item', {id: 'coverstyle', href: 'Styles/coverstyle.css', 'media-type': 'text/css'}),
m('item', {id: 'titlestyle', href: 'Styles/titlestyle.css', 'media-type': 'text/css'}),
m('item', {id: 'navstyle', href: 'Styles/navstyle.css', 'media-type': 'text/css'}),
m('item', { id: 'style', href: 'Styles/style.css', 'media-type': 'text/css' }),
m('item', { id: 'coverstyle', href: 'Styles/coverstyle.css', 'media-type': 'text/css' }),
m('item', { id: 'titlestyle', href: 'Styles/titlestyle.css', 'media-type': 'text/css' }),
m('item', { id: 'navstyle', href: 'Styles/navstyle.css', 'media-type': 'text/css' }),
ffc.iconsFont ? m('item', {id: 'font-awesome', href: 'Fonts/fontawesome-webfont-subset.ttf', 'media-type': 'application/x-font-ttf'}) : null,
ffc.iconsFont ? m('item', { id: 'font-awesome', href: 'Fonts/fontawesome-webfont-subset.ttf', 'media-type': 'application/x-font-ttf' }) : null,
m('item', {id: 'coverpage', href: 'Text/cover.xhtml', 'media-type': 'application/xhtml+xml', properties: ffc.coverImage ? 'svg' : undefined}),
m('item', {id: 'titlepage', href: 'Text/title.xhtml', 'media-type': 'application/xhtml+xml', properties: ffc.hasRemoteResources.titlePage ? 'remote-resources' : null})
m('item', { id: 'coverpage', href: 'Text/cover.xhtml', 'media-type': 'application/xhtml+xml', properties: ffc.coverImage ? 'svg' : undefined }),
m('item', { id: 'titlepage', href: 'Text/title.xhtml', 'media-type': 'application/xhtml+xml', properties: ffc.hasRemoteResources.titlePage ? 'remote-resources' : null })
].concat(manifestChapters, manifestNotes, remotes)),
m('spine', {toc: 'ncx'}, sortSpineItems([
m('itemref', {idref: 'coverpage'}),
m('itemref', {idref: 'titlepage'}),
m('itemref', {idref: 'nav', linear: ffc.storyInfo.chapters.length <= 1 && !(ffc.options.includeAuthorNotes && ffc.options.useAuthorNotesIndex && ffc.hasAuthorNotes) ? 'no' : undefined})
m('spine', { toc: 'ncx' }, sortSpineItems([
m('itemref', { idref: 'coverpage' }),
m('itemref', { idref: 'titlepage' }),
m('itemref', { idref: 'nav', linear: ffc.storyInfo.chapters.length <= 1 && !(ffc.options.includeAuthorNotes && ffc.options.useAuthorNotesIndex && ffc.hasAuthorNotes) ? 'no' : undefined })
].concat(
spineChapters,
spineNotes
))),
m('guide', [
m('reference', {type: 'cover', title: 'Cover', href: 'Text/cover.xhtml'}),
m('reference', {type: 'toc', title: 'Contents', href: 'nav.xhtml'})
m('reference', { type: 'cover', title: 'Cover', href: 'Text/cover.xhtml' }),
m('reference', { type: 'toc', title: 'Contents', href: 'nav.xhtml' })
])
])
, {strict: true}).then((contentOpf) => {
, { strict: true }).then((contentOpf) => {
contentOpf = '<?xml version="1.0" encoding="utf-8"?>\n' + pretty.xml(contentOpf)
return contentOpf
})
@ -255,21 +255,21 @@ function navPoints (list) {
let arr = []
for (let i = 0; i < list.length; i++) {
if (!list[i]) continue
arr.push(m('navPoint', {id: 'navPoint-' + (i + 1), playOrder: playOrder++}, [
arr.push(m('navPoint', { id: 'navPoint-' + (i + 1), playOrder: playOrder++ }, [
m('navLabel', m('text', list[i][0])),
m('content', {src: list[i][1]})
m('content', { src: list[i][1] })
]))
}
return arr
}
export function createNcx (ffc) {
return render(
m('ncx', {version: '2005-1', xmlns: NS.DAISY}, [
m('ncx', { version: '2005-1', xmlns: NS.DAISY }, [
m('head', [
m('meta', {content: ffc.storyInfo.uuid, name: 'dtb:uid'}),
m('meta', {content: 0, name: 'dtb:depth'}),
m('meta', {content: 0, name: 'dtb:totalPageCount'}),
m('meta', {content: 0, name: 'dtb:maxPageNumber'})
m('meta', { content: ffc.storyInfo.uuid, name: 'dtb:uid' }),
m('meta', { content: 0, name: 'dtb:depth' }),
m('meta', { content: 0, name: 'dtb:totalPageCount' }),
m('meta', { content: 0, name: 'dtb:maxPageNumber' })
]),
m('docTitle', m('text', ffc.storyInfo.title)),
m('navMap', navPoints([
@ -280,7 +280,7 @@ export function createNcx (ffc) {
[ch.title, 'Text/chapter_' + zeroFill(3, num + 1) + '.xhtml']
), ffc.options.includeAuthorNotes && ffc.options.useAuthorNotesIndex && ffc.hasAuthorNotes ? [['Author\'s Notes', 'notesnav.xhtml']] : null)))
])
, {strict: true}).then((tocNcx) => {
, { strict: true }).then((tocNcx) => {
tocNcx = '<?xml version="1.0" encoding="utf-8" ?>\n' + pretty.xml(tocNcx)
return tocNcx
})
@ -288,42 +288,42 @@ export function createNcx (ffc) {
export function createNav (ffc) {
let list = [
m('li', m('a', {href: 'Text/cover.xhtml'}, 'Cover')),
m('li', m('a', {href: 'Text/title.xhtml'}, 'Title Page')),
ffc.storyInfo.chapters.length > 1 || (ffc.options.includeAuthorNotes && ffc.options.useAuthorNotesIndex && ffc.hasAuthorNotes) ? m('li', m('a', {href: 'nav.xhtml'}, 'Contents')) : null
m('li', m('a', { href: 'Text/cover.xhtml' }, 'Cover')),
m('li', m('a', { href: 'Text/title.xhtml' }, 'Title Page')),
ffc.storyInfo.chapters.length > 1 || (ffc.options.includeAuthorNotes && ffc.options.useAuthorNotesIndex && ffc.hasAuthorNotes) ? m('li', m('a', { href: 'nav.xhtml' }, 'Contents')) : null
].concat(ffc.storyInfo.chapters.map((ch, num) =>
m('li.leftalign', [
m('a', {href: 'Text/chapter_' + zeroFill(3, num + 1) + '.xhtml'}, ch.title)
m('a', { href: 'Text/chapter_' + zeroFill(3, num + 1) + '.xhtml' }, ch.title)
])
))
let prettyList = ffc.storyInfo.chapters.map((ch, num) =>
m('li.item', [
m('.floatbox', m('span.wordcount', ch.realWordCount.toLocaleString('en-GB'))),
m('a', {href: 'Text/chapter_' + zeroFill(3, num + 1) + '.xhtml'}, ch.title),
m('a', { href: 'Text/chapter_' + zeroFill(3, num + 1) + '.xhtml' }, ch.title),
m('span.date', [m('b', ' · '), prettyDate(new Date(ch.date_modified * 1000))])
])
)
if (ffc.options.includeAuthorNotes && ffc.options.useAuthorNotesIndex && ffc.hasAuthorNotes) {
list.push(m('li', m('a', {href: 'notesnav.xhtml'}, 'Author\'s Notes')))
prettyList.push(m('li.item.double', m('a', {href: 'notesnav.xhtml'}, 'Author\'s Notes')))
list.push(m('li', m('a', { href: 'notesnav.xhtml' }, 'Author\'s Notes')))
prettyList.push(m('li.item.double', m('a', { href: 'notesnav.xhtml' }, 'Author\'s Notes')))
}
return render(
m('html', {xmlns: NS.XHTML, 'xmlns:epub': NS.OPS, lang: 'en'}, [
m('html', { xmlns: NS.XHTML, 'xmlns:epub': NS.OPS, lang: 'en' }, [
m('head', [
m('meta', {charset: 'utf-8'}),
m('link', {rel: 'stylesheet', type: 'text/css', href: 'Styles/style.css'}),
m('link', {rel: 'stylesheet', type: 'text/css', href: 'Styles/navstyle.css'}),
m('meta', { charset: 'utf-8' }),
m('link', { rel: 'stylesheet', type: 'text/css', href: 'Styles/style.css' }),
m('link', { rel: 'stylesheet', type: 'text/css', href: 'Styles/navstyle.css' }),
m('title', 'Contents')
]),
m('body', {'epub:type': 'frontmatter toc'}, m('div', [
m('nav.invisible', {'epub:type': 'toc'}, m('ol', list)),
m('body', { 'epub:type': 'frontmatter toc' }, m('div', [
m('nav.invisible', { 'epub:type': 'toc' }, m('ol', list)),
m('h3', 'Contents'),
m('ul#toc.hidden', prettyList),
ffc.options.addChapterBars ? chapterBars(ffc.storyInfo.chapters) : null
]))
])
, {strict: true}).then((navDocument) => {
, { strict: true }).then((navDocument) => {
navDocument = '<?xml version="1.0" encoding="utf-8"?>\n<!DOCTYPE html>\n' + pretty.xml(navDocument)
return navDocument
})
@ -332,23 +332,23 @@ export function createNav (ffc) {
export function createNotesNav (ffc) {
let list = ffc.chaptersWithNotes.map((num) => {
let ch = ffc.storyInfo.chapters[num]
return m('.item', m('a.leftalign', {href: 'Text/note_' + zeroFill(3, num + 1) + '.xhtml'}, ch.title))
return m('.item', m('a.leftalign', { href: 'Text/note_' + zeroFill(3, num + 1) + '.xhtml' }, ch.title))
})
return render(
m('html', {xmlns: NS.XHTML, 'xmlns:epub': NS.OPS, lang: 'en'}, [
m('html', { xmlns: NS.XHTML, 'xmlns:epub': NS.OPS, lang: 'en' }, [
m('head', [
m('meta', {charset: 'utf-8'}),
m('link', {rel: 'stylesheet', type: 'text/css', href: 'Styles/style.css'}),
m('link', {rel: 'stylesheet', type: 'text/css', href: 'Styles/navstyle.css'}),
m('meta', { charset: 'utf-8' }),
m('link', { rel: 'stylesheet', type: 'text/css', href: 'Styles/style.css' }),
m('link', { rel: 'stylesheet', type: 'text/css', href: 'Styles/navstyle.css' }),
m('title', 'Author\'s Notes')
]),
m('body#navpage', {'epub:type': 'frontmatter toc'}, m('div', [
m('body#navpage', { 'epub:type': 'frontmatter toc' }, m('div', [
m('h3', 'Author\'s Notes'),
m('#toc', list)
]))
])
, {strict: true}).then((navDocument) => {
, { strict: true }).then((navDocument) => {
navDocument = '<?xml version="1.0" encoding="utf-8"?>\n<!DOCTYPE html>\n' + pretty.xml(navDocument)
return navDocument
})
@ -357,11 +357,11 @@ export function createNotesNav (ffc) {
export function createCoverPage (ffc) {
let body
let {width, height} = ffc.coverImageDimensions
let { width, height } = ffc.coverImageDimensions
if (ffc.coverImage) {
body = m('svg#cover', {xmlns: NS.SVG, 'xmlns:xlink': NS.XLINK, version: '1.1', viewBox: '0 0 ' + width + ' ' + height},
m('image', {width: width, height: height, 'xlink:href': '../' + ffc.coverFilename})
body = m('svg#cover', { xmlns: NS.SVG, 'xmlns:xlink': NS.XLINK, version: '1.1', viewBox: '0 0 ' + width + ' ' + height },
m('image', { width: width, height: height, 'xlink:href': '../' + ffc.coverFilename })
)
} else {
body = [
@ -371,22 +371,22 @@ export function createCoverPage (ffc) {
}
return render(
m('html', {xmlns: NS.XHTML, 'xmlns:epub': NS.OPS, lang: 'en'}, [
m('html', { xmlns: NS.XHTML, 'xmlns:epub': NS.OPS, lang: 'en' }, [
m('head', [
ffc.coverImage ? m('meta', {name: 'viewport', content: 'width=' + width + ', height=' + height}) : null,
ffc.coverImage ? m('meta', { name: 'viewport', content: 'width=' + width + ', height=' + height }) : null,
m('title', 'Cover'),
m('link', {rel: 'stylesheet', type: 'text/css', href: '../Styles/coverstyle.css'})
m('link', { rel: 'stylesheet', type: 'text/css', href: '../Styles/coverstyle.css' })
]),
m('body#coverpage', {'epub:type': 'frontmatter cover'}, body)
m('body#coverpage', { 'epub:type': 'frontmatter cover' }, body)
])
, {strict: true}).then((coverPage) => {
, { strict: true }).then((coverPage) => {
coverPage = '<?xml version="1.0" encoding="utf-8"?>\n<!DOCTYPE html>\n' + pretty.xml(coverPage)
return coverPage
})
}
function infoBox (heading, data, title) {
return m('.infobox', {title}, m('.wrap', [
return m('.infobox', { title }, m('.wrap', [
m('span.heading', heading),
m('br'),
m('span.data', data)
@ -425,33 +425,33 @@ export function createTitlePage (ffc) {
}
return render(
m('html', {xmlns: NS.XHTML, 'xmlns:epub': NS.OPS, lang: 'en'}, [
m('html', { xmlns: NS.XHTML, 'xmlns:epub': NS.OPS, lang: 'en' }, [
m('head', [
m('meta', {charset: 'utf-8'}),
m('link', {rel: 'stylesheet', type: 'text/css', href: '../Styles/style.css'}),
m('link', {rel: 'stylesheet', type: 'text/css', href: '../Styles/titlestyle.css'}),
m('meta', { charset: 'utf-8' }),
m('link', { rel: 'stylesheet', type: 'text/css', href: '../Styles/style.css' }),
m('link', { rel: 'stylesheet', type: 'text/css', href: '../Styles/titlestyle.css' }),
m('title', ffc.storyInfo.title)
]),
m('body#titlepage', {'epub:type': 'frontmatter titlepage'}, m('div', [
m('body#titlepage', { 'epub:type': 'frontmatter titlepage' }, m('div', [
m('header.title', [
m('div', {className: 'content-rating content-rating-' + ffc.storyInfo.content_rating_text.toLowerCase()}, ffc.storyInfo.content_rating_text.charAt(0).toUpperCase()),
m('div', { className: 'content-rating content-rating-' + ffc.storyInfo.content_rating_text.toLowerCase() }, ffc.storyInfo.content_rating_text.charAt(0).toUpperCase()),
m('span.story_name', ffc.storyInfo.title + ' '),
m('span.author', ['by ', m('b', ffc.storyInfo.author.name)])
]),
// m('hr'),
m('.tags', ffc.tags.filter((tag) => tag.type !== 'character').map((tag) =>
[m('span.tagbox', m('span', {className: tag.className}, tag.name))]
[m('span.tagbox', m('span', { className: tag.className }, tag.name))]
)),
m('.readlink', m('a', {href: ffc.storyInfo.url}, 'Story on Fimfiction')),
m('.readlink', m('a', { href: ffc.storyInfo.url }, 'Story on Fimfiction')),
// m('hr'),
ffc.storyInfo.prequel ? [m('div', [
m('br'),
'This story is a sequel to ',
m('a', {href: ffc.storyInfo.prequel.url}, ffc.storyInfo.prequel.title)
m('a', { href: ffc.storyInfo.prequel.url }, ffc.storyInfo.prequel.title)
]), m('hr.old')] : null,
m('#description', tokenContent),
m('.bottom', [
m('section', {className: 'completed-status completed-status-' + ffc.storyInfo.status.toLowerCase()}, [
m('section', { className: 'completed-status completed-status-' + ffc.storyInfo.status.toLowerCase() }, [
m('i.fa.fa-fw.fa-' + completedIcon[ffc.storyInfo.status.toLowerCase()], ' '),
ffc.storyInfo.status
]),
@ -463,11 +463,11 @@ export function createTitlePage (ffc) {
]),
// m('hr'),
m('.tags', ffc.tags.filter((tag) => tag.type === 'character').map((tag) =>
[m('span.tagbox', m('span', {className: tag.className}, tag.name))]
[m('span.tagbox', m('span', { className: tag.className }, tag.name))]
))
]))
])
, {strict: true}).then((titlePage) => {
, { strict: true }).then((titlePage) => {
titlePage = '<?xml version="1.0" encoding="utf-8"?>\n<!DOCTYPE html>\n' + titlePage
titlePage = titlePage.replace(tokenContent, '\n' + ffc.storyInfo.description + '\n')
return titlePage

View file

@ -183,7 +183,7 @@ export function typogrify (content) {
let closeMatch
const reSkipTags = /<(\/)?(style|pre|code|kbd|script|math|title)[^>]*>/i
content = typogr.tokenize(content).map(({type, txt}) => {
content = typogr.tokenize(content).map(({ type, txt }) => {
if (type === 'tag') {
closeMatch = reSkipTags.exec(txt)
if (closeMatch && closeMatch[1] === undefined) {

View file

@ -122,7 +122,7 @@ const bundleNpmModuleConfig = {
__dirname: false
},
externals: [nodeExternals({whitelist: [/^babel-runtime/, /fontawesome-webfont\.ttf/]})],
externals: [nodeExternals({ whitelist: [/^babel-runtime/, /fontawesome-webfont\.ttf/] })],
plugins: [],
performance: {