mirror of
https://github.com/daniel-j/fimfic2epub.git
synced 2024-05-19 20:03:53 +12:00
lint
This commit is contained in:
parent
19194dea31
commit
9a2e60c1c9
|
@ -33,7 +33,7 @@ if (isStandalone) {
|
||||||
webpackConfig.pop()
|
webpackConfig.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
let watchOpts = {
|
const watchOpts = {
|
||||||
readDelay: 500,
|
readDelay: 500,
|
||||||
verbose: true,
|
verbose: true,
|
||||||
read: false
|
read: false
|
||||||
|
@ -41,12 +41,12 @@ let watchOpts = {
|
||||||
|
|
||||||
let packageVersion = require('./package.json').version
|
let packageVersion = require('./package.json').version
|
||||||
|
|
||||||
let webpackDefines = new webpack.DefinePlugin({
|
const webpackDefines = new webpack.DefinePlugin({
|
||||||
FIMFIC2EPUB_VERSION: JSON.stringify(packageVersion)
|
FIMFIC2EPUB_VERSION: JSON.stringify(packageVersion)
|
||||||
})
|
})
|
||||||
|
|
||||||
// No need to bloat the build with a list of all tlds...
|
// No need to bloat the build with a list of all tlds...
|
||||||
let replaceTlds = new webpack.NormalModuleReplacementPlugin(/^tlds$/, '../../src/false')
|
const replaceTlds = new webpack.NormalModuleReplacementPlugin(/^tlds$/, '../../src/false')
|
||||||
|
|
||||||
webpackConfig.forEach((c) => {
|
webpackConfig.forEach((c) => {
|
||||||
c.plugins.push(webpackDefines)
|
c.plugins.push(webpackDefines)
|
||||||
|
@ -89,8 +89,8 @@ function webpackTask () {
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertFontAwesomeVars (contents) {
|
function convertFontAwesomeVars (contents) {
|
||||||
let vars = {}
|
const vars = {}
|
||||||
let matchVar = /\$fa-var-(.*?): "\\(.*?)";/g
|
const matchVar = /\$fa-var-(.*?): "\\(.*?)";/g
|
||||||
let ma
|
let ma
|
||||||
for (;(ma = matchVar.exec(contents));) {
|
for (;(ma = matchVar.exec(contents));) {
|
||||||
vars[ma[1]] = String.fromCharCode(parseInt(ma[2], 16))
|
vars[ma[1]] = String.fromCharCode(parseInt(ma[2], 16))
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
"url": "https://github.com/daniel-j/fimfic2epub.git"
|
"url": "https://github.com/daniel-j/fimfic2epub.git"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "NODE_ENV=production gulp",
|
"build": "gulp",
|
||||||
"dev": "NODE_ENV=development gulp"
|
"dev": "NODE_ENV=development gulp"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
|
@ -31,9 +31,9 @@ const trimWhitespace = /^\s*(<br\s*\/?\s*>)+|(<br\s*\/?\s*>)+\s*$/ig
|
||||||
class FimFic2Epub extends EventEmitter {
|
class FimFic2Epub extends EventEmitter {
|
||||||
static getStoryId (id) {
|
static getStoryId (id) {
|
||||||
if (isNaN(id)) {
|
if (isNaN(id)) {
|
||||||
let url = URL(id)
|
const url = URL(id)
|
||||||
if (url.hostname === 'www.fimfiction.net' || url.hostname === 'fimfiction.net') {
|
if (url.hostname === 'www.fimfiction.net' || url.hostname === 'fimfiction.net') {
|
||||||
let m = url.pathname.match(/^\/story\/(\d+)/)
|
const m = url.pathname.match(/^\/story\/(\d+)/)
|
||||||
if (m) {
|
if (m) {
|
||||||
id = m[1]
|
id = m[1]
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ class FimFic2Epub extends EventEmitter {
|
||||||
static fetchStoryInfo (storyId, raw = false) {
|
static fetchStoryInfo (storyId, raw = false) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
storyId = FimFic2Epub.getStoryId(storyId)
|
storyId = FimFic2Epub.getStoryId(storyId)
|
||||||
let url = '/api/story.php?story=' + storyId
|
const url = '/api/story.php?story=' + storyId
|
||||||
fetch(url).then((content) => {
|
fetch(url).then((content) => {
|
||||||
let data
|
let data
|
||||||
try {
|
try {
|
||||||
|
@ -63,7 +63,7 @@ class FimFic2Epub extends EventEmitter {
|
||||||
reject(new Error(data.error + ' (id: ' + storyId + ')'))
|
reject(new Error(data.error + ' (id: ' + storyId + ')'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let story = data.story
|
const story = data.story
|
||||||
if (raw) {
|
if (raw) {
|
||||||
resolve(story)
|
resolve(story)
|
||||||
return
|
return
|
||||||
|
@ -223,20 +223,20 @@ class FimFic2Epub extends EventEmitter {
|
||||||
|
|
||||||
this.progress(0, 0, 'Fetching chapters...')
|
this.progress(0, 0, 'Fetching chapters...')
|
||||||
|
|
||||||
let chapterCount = this.storyInfo.chapters.length
|
const chapterCount = this.storyInfo.chapters.length
|
||||||
let url = 'https://fimfiction.net/story/download/' + this.storyInfo.id + '/html'
|
const url = 'https://fimfiction.net/story/download/' + this.storyInfo.id + '/html'
|
||||||
|
|
||||||
this.pcache.chapters = fetch(url).then((html) => {
|
this.pcache.chapters = fetch(url).then((html) => {
|
||||||
let p = Promise.resolve()
|
let p = Promise.resolve()
|
||||||
let matchChapter = /<article class="chapter">[\s\S]*?<\/header>([\s\S]*?)<\/article>/g
|
const matchChapter = /<article class="chapter">[\s\S]*?<\/header>([\s\S]*?)<\/article>/g
|
||||||
for (let ma, i = 0; (ma = matchChapter.exec(html)); i++) {
|
for (let ma, i = 0; (ma = matchChapter.exec(html)); i++) {
|
||||||
const ch = this.storyInfo.chapters[i]
|
const ch = this.storyInfo.chapters[i]
|
||||||
let chapterContent = ma[1]
|
let chapterContent = ma[1]
|
||||||
chapterContent = chapterContent.replace(/<footer>[\s\S]*?<\/footer>/g, '').trim()
|
chapterContent = chapterContent.replace(/<footer>[\s\S]*?<\/footer>/g, '').trim()
|
||||||
|
|
||||||
let authorNotesPos = chapterContent.indexOf('<aside ')
|
const authorNotesPos = chapterContent.indexOf('<aside ')
|
||||||
let notesContent = ''
|
let notesContent = ''
|
||||||
let notesFirst = authorNotesPos === 0
|
const notesFirst = authorNotesPos === 0
|
||||||
if (authorNotesPos !== -1) {
|
if (authorNotesPos !== -1) {
|
||||||
chapterContent = chapterContent.replace(/<aside class="authors-note">([\s\S]*?)<\/aside>/, (match, content, pos) => {
|
chapterContent = chapterContent.replace(/<aside class="authors-note">([\s\S]*?)<\/aside>/, (match, content, pos) => {
|
||||||
content = content.replace(/<header><h1>.*?<\/h1><\/header>/, '')
|
content = content.replace(/<header><h1>.*?<\/h1><\/header>/, '')
|
||||||
|
@ -290,14 +290,14 @@ class FimFic2Epub extends EventEmitter {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
let checksums = new Map()
|
const checksums = new Map()
|
||||||
|
|
||||||
this.progress(0, 0, 'Fetching remote files...')
|
this.progress(0, 0, 'Fetching remote files...')
|
||||||
this.pcache.remoteResources = new Promise((resolve, reject) => {
|
this.pcache.remoteResources = new Promise((resolve, reject) => {
|
||||||
let iter = this.remoteResources.entries()
|
const iter = this.remoteResources.entries()
|
||||||
let completeCount = 0
|
let completeCount = 0
|
||||||
|
|
||||||
let next = (r) => {
|
const next = (r) => {
|
||||||
completeCount++
|
completeCount++
|
||||||
if (r.data) {
|
if (r.data) {
|
||||||
this.progress(0, completeCount / this.remoteResources.size, 'Fetched remote file ' + completeCount + ' / ' + this.remoteResources.size)
|
this.progress(0, completeCount / this.remoteResources.size, 'Fetched remote file ' + completeCount + ' / ' + this.remoteResources.size)
|
||||||
|
@ -307,7 +307,7 @@ class FimFic2Epub extends EventEmitter {
|
||||||
recursive()
|
recursive()
|
||||||
}
|
}
|
||||||
|
|
||||||
let recursive = () => {
|
const recursive = () => {
|
||||||
let r = iter.next().value
|
let r = iter.next().value
|
||||||
if (!r) {
|
if (!r) {
|
||||||
if (completeCount === this.remoteResources.size) {
|
if (completeCount === this.remoteResources.size) {
|
||||||
|
@ -315,7 +315,7 @@ class FimFic2Epub extends EventEmitter {
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let url = r[0]
|
const url = r[0]
|
||||||
r = r[1]
|
r = r[1]
|
||||||
if (r.data) {
|
if (r.data) {
|
||||||
next(r)
|
next(r)
|
||||||
|
@ -335,9 +335,9 @@ class FimFic2Epub extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (info) {
|
if (info) {
|
||||||
let checksum = crc32(isNode ? data : new Uint8Array(data))
|
const checksum = crc32(isNode ? data : new Uint8Array(data))
|
||||||
if (checksums.has(checksum)) {
|
if (checksums.has(checksum)) {
|
||||||
let sameFile = this.remoteResources.get(checksums.get(checksum))
|
const sameFile = this.remoteResources.get(checksums.get(checksum))
|
||||||
r.dest = sameFile.dest
|
r.dest = sameFile.dest
|
||||||
r.filename = sameFile.dest
|
r.filename = sameFile.dest
|
||||||
r.type = sameFile.type
|
r.type = sameFile.type
|
||||||
|
@ -348,11 +348,11 @@ class FimFic2Epub extends EventEmitter {
|
||||||
data = await utils.webp2png(isNode ? data : new Uint8Array(data))
|
data = await utils.webp2png(isNode ? data : new Uint8Array(data))
|
||||||
info = fileType(data)
|
info = fileType(data)
|
||||||
}
|
}
|
||||||
let type = info.mime
|
const type = info.mime
|
||||||
r.type = type
|
r.type = type
|
||||||
let isImage = type.startsWith('image/')
|
const isImage = type.startsWith('image/')
|
||||||
let folder = isImage ? 'Images' : 'Misc'
|
const folder = isImage ? 'Images' : 'Misc'
|
||||||
let dest = folder + '/*.' + info.ext
|
const dest = folder + '/*.' + info.ext
|
||||||
r.dest = dest.replace('*', r.filename)
|
r.dest = dest.replace('*', r.filename)
|
||||||
r.data = data
|
r.data = data
|
||||||
}
|
}
|
||||||
|
@ -393,8 +393,8 @@ class FimFic2Epub extends EventEmitter {
|
||||||
this.notesHtml.length = 0
|
this.notesHtml.length = 0
|
||||||
|
|
||||||
for (let i = 0; i < this.chapters.length; i++) {
|
for (let i = 0; i < this.chapters.length; i++) {
|
||||||
let ch = this.storyInfo.chapters[i]
|
const ch = this.storyInfo.chapters[i]
|
||||||
let chapter = this.chapters[i]
|
const chapter = this.chapters[i]
|
||||||
let content = chapter.content
|
let content = chapter.content
|
||||||
|
|
||||||
if (this.options.typogrify) {
|
if (this.options.typogrify) {
|
||||||
|
@ -486,8 +486,8 @@ class FimFic2Epub extends EventEmitter {
|
||||||
this.zip.file('OEBPS/Styles/navstyle.css', Buffer.from(navstyleCss, 'utf8'))
|
this.zip.file('OEBPS/Styles/navstyle.css', Buffer.from(navstyleCss, 'utf8'))
|
||||||
|
|
||||||
for (let i = 0; i < this.chapters.length; i++) {
|
for (let i = 0; i < this.chapters.length; i++) {
|
||||||
let filename = 'OEBPS/Text/chapter_' + zeroFill(3, i + 1) + '.xhtml'
|
const filename = 'OEBPS/Text/chapter_' + zeroFill(3, i + 1) + '.xhtml'
|
||||||
let html = this.chaptersHtml[i]
|
const html = this.chaptersHtml[i]
|
||||||
this.zip.file(filename, Buffer.from(html, 'utf8'))
|
this.zip.file(filename, Buffer.from(html, 'utf8'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -496,8 +496,8 @@ class FimFic2Epub extends EventEmitter {
|
||||||
|
|
||||||
for (let i = 0; i < this.chapters.length; i++) {
|
for (let i = 0; i < this.chapters.length; i++) {
|
||||||
if (!this.chapters[i].notes) continue
|
if (!this.chapters[i].notes) continue
|
||||||
let filename = 'OEBPS/Text/note_' + zeroFill(3, i + 1) + '.xhtml'
|
const filename = 'OEBPS/Text/note_' + zeroFill(3, i + 1) + '.xhtml'
|
||||||
let html = this.notesHtml[i]
|
const html = this.notesHtml[i]
|
||||||
this.zip.file(filename, Buffer.from(html, 'utf8'))
|
this.zip.file(filename, Buffer.from(html, 'utf8'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -511,7 +511,7 @@ class FimFic2Epub extends EventEmitter {
|
||||||
(paragraphsCss[this.options.paragraphStyle] || '')
|
(paragraphsCss[this.options.paragraphStyle] || '')
|
||||||
, 'utf8'))
|
, 'utf8'))
|
||||||
|
|
||||||
let remoteDestCache = new Set()
|
const remoteDestCache = new Set()
|
||||||
this.remoteResources.forEach((r) => {
|
this.remoteResources.forEach((r) => {
|
||||||
if (r.dest && !remoteDestCache.has(r.dest)) {
|
if (r.dest && !remoteDestCache.has(r.dest)) {
|
||||||
this.zip.file('OEBPS/' + r.dest, r.data)
|
this.zip.file('OEBPS/' + r.dest, r.data)
|
||||||
|
@ -541,7 +541,7 @@ class FimFic2Epub extends EventEmitter {
|
||||||
compression: 'DEFLATE',
|
compression: 'DEFLATE',
|
||||||
compressionOptions: { level: 9 }
|
compressionOptions: { level: 9 }
|
||||||
}, (metadata) => { // onUpdate
|
}, (metadata) => { // onUpdate
|
||||||
let currentPercent = Math.round(metadata.percent / 10) * 10
|
const currentPercent = Math.round(metadata.percent / 10) * 10
|
||||||
if (lastPercent !== currentPercent) {
|
if (lastPercent !== currentPercent) {
|
||||||
lastPercent = currentPercent
|
lastPercent = currentPercent
|
||||||
this.progress(0, currentPercent / 100, 'Compressing...')
|
this.progress(0, currentPercent / 100, 'Compressing...')
|
||||||
|
@ -575,7 +575,7 @@ class FimFic2Epub extends EventEmitter {
|
||||||
compressionOptions: { level: 9 }
|
compressionOptions: { level: 9 }
|
||||||
}, (metadata) => {
|
}, (metadata) => {
|
||||||
if (onUpdate) onUpdate(metadata)
|
if (onUpdate) onUpdate(metadata)
|
||||||
let currentPercent = Math.round(metadata.percent / 20) * 20
|
const currentPercent = Math.round(metadata.percent / 20) * 20
|
||||||
if (lastPercent !== currentPercent) {
|
if (lastPercent !== currentPercent) {
|
||||||
lastPercent = currentPercent
|
lastPercent = currentPercent
|
||||||
this.progress(0, currentPercent / 100, 'Compressing...')
|
this.progress(0, currentPercent / 100, 'Compressing...')
|
||||||
|
@ -587,13 +587,15 @@ class FimFic2Epub extends EventEmitter {
|
||||||
this.storyInfo.title = title.trim()
|
this.storyInfo.title = title.trim()
|
||||||
this.filename = FimFic2Epub.getFilename(this.storyInfo)
|
this.filename = FimFic2Epub.getFilename(this.storyInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
setAuthorName (name) {
|
setAuthorName (name) {
|
||||||
this.storyInfo.author.name = name.trim()
|
this.storyInfo.author.name = name.trim()
|
||||||
this.filename = FimFic2Epub.getFilename(this.storyInfo)
|
this.filename = FimFic2Epub.getFilename(this.storyInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
setCoverImage (buffer) {
|
setCoverImage (buffer) {
|
||||||
buffer = isNode ? buffer : Buffer.from(new Uint8Array(buffer))
|
buffer = isNode ? buffer : Buffer.from(new Uint8Array(buffer))
|
||||||
let info = fileType(buffer)
|
const info = fileType(buffer)
|
||||||
if (!info || !info.mime.startsWith('image/')) {
|
if (!info || !info.mime.startsWith('image/')) {
|
||||||
throw new Error('Invalid image')
|
throw new Error('Invalid image')
|
||||||
}
|
}
|
||||||
|
@ -620,21 +622,21 @@ class FimFic2Epub extends EventEmitter {
|
||||||
|
|
||||||
findRemoteResources (prefix, where, html) {
|
findRemoteResources (prefix, where, html) {
|
||||||
let remoteCounter = 1
|
let remoteCounter = 1
|
||||||
let matchUrl = /<img.*?src="([^">]*\/([^">]*?))".*?>/g
|
const matchUrl = /<img.*?src="([^">]*\/([^">]*?))".*?>/g
|
||||||
let emoticonUrl = /static\.fimfiction\.net\/images\/emoticons\/([a-z_]*)\.[a-z]*$/
|
const emoticonUrl = /static\.fimfiction\.net\/images\/emoticons\/([a-z_]*)\.[a-z]*$/
|
||||||
|
|
||||||
for (let ma; (ma = matchUrl.exec(html));) {
|
for (let ma; (ma = matchUrl.exec(html));) {
|
||||||
let url = ma[1]
|
const url = ma[1]
|
||||||
let cleanurl = entities.decode(url)
|
const cleanurl = entities.decode(url)
|
||||||
if (this.remoteResources.has(cleanurl)) {
|
if (this.remoteResources.has(cleanurl)) {
|
||||||
let r = this.remoteResources.get(cleanurl)
|
const r = this.remoteResources.get(cleanurl)
|
||||||
if (r.where.indexOf(where) === -1) {
|
if (r.where.indexOf(where) === -1) {
|
||||||
r.where.push(where)
|
r.where.push(where)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
let filename = prefix + '_' + remoteCounter
|
let filename = prefix + '_' + remoteCounter
|
||||||
let emoticon = url.match(emoticonUrl)
|
const emoticon = url.match(emoticonUrl)
|
||||||
if (emoticon) {
|
if (emoticon) {
|
||||||
filename = 'emoticon_' + emoticon[1]
|
filename = 'emoticon_' + emoticon[1]
|
||||||
}
|
}
|
||||||
|
@ -644,7 +646,7 @@ class FimFic2Epub extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
async findIcons () {
|
async findIcons () {
|
||||||
let matchIcon = /<i class="fa fa-fw fa-(.*?)"/g
|
const matchIcon = /<i class="fa fa-fw fa-(.*?)"/g
|
||||||
this.usedIcons.clear()
|
this.usedIcons.clear()
|
||||||
|
|
||||||
const scan = (html) => {
|
const scan = (html) => {
|
||||||
|
@ -667,10 +669,10 @@ class FimFic2Epub extends EventEmitter {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let glyphs = [...this.usedIcons].map((name) => {
|
const glyphs = [...this.usedIcons].map((name) => {
|
||||||
return fontAwesomeCodes[name].charCodeAt(0)
|
return fontAwesomeCodes[name].charCodeAt(0)
|
||||||
})
|
})
|
||||||
let fontFile = require('font-awesome/fonts/fontawesome-webfont.ttf')
|
const fontFile = require('font-awesome/fonts/fontawesome-webfont.ttf')
|
||||||
this.iconsFont = await subsetFont(fontFile, glyphs, { local: isNode })
|
this.iconsFont = await subsetFont(fontFile, glyphs, { local: isNode })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -692,7 +694,7 @@ class FimFic2Epub extends EventEmitter {
|
||||||
return Promise.resolve(this.coverImage)
|
return Promise.resolve(this.coverImage)
|
||||||
}
|
}
|
||||||
this.coverImage = null
|
this.coverImage = null
|
||||||
let url = this.coverUrl || this.storyInfo.full_image
|
const url = this.coverUrl || this.storyInfo.full_image
|
||||||
if (!url) {
|
if (!url) {
|
||||||
console.warn('Story has no image. Generating one...')
|
console.warn('Story has no image. Generating one...')
|
||||||
let canvas
|
let canvas
|
||||||
|
@ -703,7 +705,7 @@ class FimFic2Epub extends EventEmitter {
|
||||||
canvas.width = 1080
|
canvas.width = 1080
|
||||||
canvas.height = 1440
|
canvas.height = 1440
|
||||||
}
|
}
|
||||||
let ctx = canvas.getContext('2d')
|
const ctx = canvas.getContext('2d')
|
||||||
ctx.fillStyle = 'white'
|
ctx.fillStyle = 'white'
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
||||||
|
|
||||||
|
@ -714,8 +716,8 @@ class FimFic2Epub extends EventEmitter {
|
||||||
ctx.strokeRect(20, 20, canvas.width - 40, canvas.height - 40)
|
ctx.strokeRect(20, 20, canvas.width - 40, canvas.height - 40)
|
||||||
ctx.strokeRect(12, 12, canvas.width - 24, canvas.height - 24)
|
ctx.strokeRect(12, 12, canvas.width - 24, canvas.height - 24)
|
||||||
|
|
||||||
let title = this.storyInfo.title
|
const title = this.storyInfo.title
|
||||||
let author = this.storyInfo.author.name
|
const author = this.storyInfo.author.name
|
||||||
|
|
||||||
let fontSize = 150
|
let fontSize = 150
|
||||||
let width
|
let width
|
||||||
|
@ -744,14 +746,14 @@ class FimFic2Epub extends EventEmitter {
|
||||||
|
|
||||||
this.pcache.coverImage = fetchRemote(url, 'arraybuffer').then((data) => {
|
this.pcache.coverImage = fetchRemote(url, 'arraybuffer').then((data) => {
|
||||||
data = isNode ? data : new Uint8Array(data)
|
data = isNode ? data : new Uint8Array(data)
|
||||||
let info = fileType(data)
|
const info = fileType(data)
|
||||||
if (info) {
|
if (info) {
|
||||||
let type = info.mime
|
const type = info.mime
|
||||||
let isImage = type.startsWith('image/')
|
const isImage = type.startsWith('image/')
|
||||||
if (!isImage) {
|
if (!isImage) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
let filename = 'Images/cover.' + info.ext
|
const filename = 'Images/cover.' + info.ext
|
||||||
this.coverFilename = filename
|
this.coverFilename = filename
|
||||||
this.coverType = type
|
this.coverType = type
|
||||||
|
|
||||||
|
@ -771,7 +773,7 @@ class FimFic2Epub extends EventEmitter {
|
||||||
|
|
||||||
fetchTitlePage () {
|
fetchTitlePage () {
|
||||||
let viewMature = true
|
let viewMature = true
|
||||||
let isStoryMature = this.storyInfo.content_rating === 2
|
const isStoryMature = this.storyInfo.content_rating === 2
|
||||||
if (!isNode) {
|
if (!isNode) {
|
||||||
viewMature = document.cookie.split('; ').includes('view_mature=true')
|
viewMature = document.cookie.split('; ').includes('view_mature=true')
|
||||||
if (!viewMature && isStoryMature) {
|
if (!viewMature && isStoryMature) {
|
||||||
|
@ -796,19 +798,19 @@ class FimFic2Epub extends EventEmitter {
|
||||||
startTagsPos += html.substring(startTagsPos).indexOf('<ul class="story-tags">') + 23
|
startTagsPos += html.substring(startTagsPos).indexOf('<ul class="story-tags">') + 23
|
||||||
let tagsHtml = html.substring(startTagsPos)
|
let tagsHtml = html.substring(startTagsPos)
|
||||||
|
|
||||||
let endTagsPos = tagsHtml.indexOf('</ul>')
|
const endTagsPos = tagsHtml.indexOf('</ul>')
|
||||||
tagsHtml = tagsHtml.substring(0, endTagsPos)
|
tagsHtml = tagsHtml.substring(0, endTagsPos)
|
||||||
|
|
||||||
let tags = []
|
const tags = []
|
||||||
let c
|
let c
|
||||||
tags.byImage = {}
|
tags.byImage = {}
|
||||||
this.subjects.length = 0
|
this.subjects.length = 0
|
||||||
this.subjects.push('Fimfiction')
|
this.subjects.push('Fimfiction')
|
||||||
this.subjects.push(this.storyInfo.content_rating_text)
|
this.subjects.push(this.storyInfo.content_rating_text)
|
||||||
|
|
||||||
let matchTag = /<a href="([^"]*?)" class="([^"]*?)" title="[^"]*?" data-tag="([^"]*?)".*?>(.*?)<\/a>/g
|
const matchTag = /<a href="([^"]*?)" class="([^"]*?)" title="[^"]*?" data-tag="([^"]*?)".*?>(.*?)<\/a>/g
|
||||||
for (;(c = matchTag.exec(tagsHtml));) {
|
for (;(c = matchTag.exec(tagsHtml));) {
|
||||||
let cat = {
|
const cat = {
|
||||||
url: 'https://fimfiction.net' + c[1],
|
url: 'https://fimfiction.net' + c[1],
|
||||||
className: 'story-tag ' + c[2],
|
className: 'story-tag ' + c[2],
|
||||||
name: entities.decode(c[4]),
|
name: entities.decode(c[4]),
|
||||||
|
@ -831,12 +833,12 @@ class FimFic2Epub extends EventEmitter {
|
||||||
html = html.substring(html.indexOf('<hr />') + 6)
|
html = html.substring(html.indexOf('<hr />') + 6)
|
||||||
}
|
}
|
||||||
|
|
||||||
let endDescPos = html.indexOf('</span>\n')
|
const endDescPos = html.indexOf('</span>\n')
|
||||||
let description = html.substring(0, endDescPos).trim()
|
const description = html.substring(0, endDescPos).trim()
|
||||||
this.description = description
|
this.description = description
|
||||||
|
|
||||||
html = html.substring(endDescPos + 7)
|
html = html.substring(endDescPos + 7)
|
||||||
let extraPos = html.indexOf('<div class="extra_story_data">')
|
const extraPos = html.indexOf('<div class="extra_story_data">')
|
||||||
html = html.substring(extraPos + 30)
|
html = html.substring(extraPos + 30)
|
||||||
|
|
||||||
ma = html.match(/<span class="approved-date">.*?data-time="(.*?)".*?<\/span>/)
|
ma = html.match(/<span class="approved-date">.*?data-time="(.*?)".*?<\/span>/)
|
||||||
|
@ -848,7 +850,7 @@ class FimFic2Epub extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
parseChapterPage (html) {
|
parseChapterPage (html) {
|
||||||
let trimWhitespace = /^\s*(<br\s*\/?\s*>)+|(<br\s*\/?\s*>)+\s*$/ig
|
const trimWhitespace = /^\s*(<br\s*\/?\s*>)+|(<br\s*\/?\s*>)+\s*$/ig
|
||||||
|
|
||||||
let authorNotesPos = html.indexOf('<div class="authors-note"')
|
let authorNotesPos = html.indexOf('<div class="authors-note"')
|
||||||
let authorNotes = ''
|
let authorNotes = ''
|
||||||
|
@ -860,10 +862,10 @@ class FimFic2Epub extends EventEmitter {
|
||||||
authorNotes = authorNotes.replace(trimWhitespace, '')
|
authorNotes = authorNotes.replace(trimWhitespace, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
let chapterPos = html.indexOf('<div class="bbcode">')
|
const chapterPos = html.indexOf('<div class="bbcode">')
|
||||||
let chapter = html.substring(chapterPos + 20)
|
let chapter = html.substring(chapterPos + 20)
|
||||||
|
|
||||||
let pos = chapter.indexOf('\t\t</div>\n\t</div>\t\t\n\t\t\t\t\t</div>\n')
|
const pos = chapter.indexOf('\t\t</div>\n\t</div>\t\t\n\t\t\t\t\t</div>\n')
|
||||||
|
|
||||||
chapter = chapter.substring(0, pos).trim()
|
chapter = chapter.substring(0, pos).trim()
|
||||||
|
|
||||||
|
@ -877,9 +879,9 @@ class FimFic2Epub extends EventEmitter {
|
||||||
if (!this.options.includeExternal) {
|
if (!this.options.includeExternal) {
|
||||||
this.remoteResources.forEach((r, url) => {
|
this.remoteResources.forEach((r, url) => {
|
||||||
if (r.originalUrl && r.where) {
|
if (r.originalUrl && r.where) {
|
||||||
let ourl = new RegExp(escapeStringRegexp(r.originalUrl), 'g')
|
const ourl = new RegExp(escapeStringRegexp(r.originalUrl), 'g')
|
||||||
for (var i = 0; i < r.where.length; i++) {
|
for (var i = 0; i < r.where.length; i++) {
|
||||||
let w = r.where[i]
|
const w = r.where[i]
|
||||||
if (typeof w === 'number') {
|
if (typeof w === 'number') {
|
||||||
if (ourl.test(this.chapters[w])) {
|
if (ourl.test(this.chapters[w])) {
|
||||||
this.storyInfo.chapters[w].remote = true
|
this.storyInfo.chapters[w].remote = true
|
||||||
|
@ -895,10 +897,10 @@ class FimFic2Epub extends EventEmitter {
|
||||||
} else {
|
} else {
|
||||||
this.remoteResources.forEach((r, url) => {
|
this.remoteResources.forEach((r, url) => {
|
||||||
if (r.dest && r.originalUrl && r.where) {
|
if (r.dest && r.originalUrl && r.where) {
|
||||||
let dest = '../' + r.dest
|
const dest = '../' + r.dest
|
||||||
let ourl = new RegExp(escapeStringRegexp(r.originalUrl), 'g')
|
const ourl = new RegExp(escapeStringRegexp(r.originalUrl), 'g')
|
||||||
for (var i = 0; i < r.where.length; i++) {
|
for (var i = 0; i < r.where.length; i++) {
|
||||||
let w = r.where[i]
|
const w = r.where[i]
|
||||||
if (typeof w === 'object' && w.chapter !== undefined && this.chaptersHtml[w.chapter]) {
|
if (typeof w === 'object' && w.chapter !== undefined && this.chaptersHtml[w.chapter]) {
|
||||||
this.chaptersHtml[w.chapter] = this.chaptersHtml[w.chapter].replace(ourl, dest)
|
this.chaptersHtml[w.chapter] = this.chaptersHtml[w.chapter].replace(ourl, dest)
|
||||||
} else if (typeof w === 'object' && w.note !== undefined && this.notesHtml[w.note]) {
|
} else if (typeof w === 'object' && w.note !== undefined && this.notesHtml[w.note]) {
|
||||||
|
|
|
@ -44,12 +44,12 @@ export async function cleanMarkup (html) {
|
||||||
html = html.replace('<blockquote style="margin: 10px 0px; box-sizing:border-box; -moz-box-sizing:border-box;margin-left:25px; padding: 15px;background-color: #F7F7F7;border: 1px solid #AAA;width: 50%;float:right;box-shadow: 5px 5px 0px #EEE;">', '<blockquote class="right_insert">')
|
html = html.replace('<blockquote style="margin: 10px 0px; box-sizing:border-box; -moz-box-sizing:border-box;margin-left:25px; padding: 15px;background-color: #F7F7F7;border: 1px solid #AAA;width: 50%;float:right;box-shadow: 5px 5px 0px #EEE;">', '<blockquote class="right_insert">')
|
||||||
|
|
||||||
// add alt attributes to images that don't have them
|
// add alt attributes to images that don't have them
|
||||||
let imageEmbed = /<img src="(.*?)" \/>/g
|
const 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
|
// Fix links pointing to pages on fimfiction
|
||||||
// Example: <a href="/user/djazz" rel="nofollow">djazz</a>
|
// Example: <a href="/user/djazz" rel="nofollow">djazz</a>
|
||||||
let matchLink = /(<a .?href=")(.+?)(".+?>)/g
|
const matchLink = /(<a .?href=")(.+?)(".+?>)/g
|
||||||
html = html.replace(matchLink, (match, head, url, tail) => {
|
html = html.replace(matchLink, (match, head, url, tail) => {
|
||||||
if (url.substring(0, 1) !== '#' && url.substring(0, 2) !== '//' && url.substring(0, 4) !== 'http' && url.substring(0, 1) === '/') {
|
if (url.substring(0, 1) !== '#' && url.substring(0, 2) !== '//' && url.substring(0, 4) !== 'http' && url.substring(0, 1) === '/') {
|
||||||
url = 'https://fimfiction.net' + url
|
url = 'https://fimfiction.net' + url
|
||||||
|
@ -62,14 +62,14 @@ export async function cleanMarkup (html) {
|
||||||
const query = new Map()
|
const query = new Map()
|
||||||
let completeCount = 0
|
let completeCount = 0
|
||||||
|
|
||||||
let matchYouTube = /<p><a class="embed" href="https:\/\/www\.youtube\.com\/watch\?v=(.*?)">.*?<\/a><\/p>/g
|
const matchYouTube = /<p><a class="embed" href="https:\/\/www\.youtube\.com\/watch\?v=(.*?)">.*?<\/a><\/p>/g
|
||||||
for (let ma; (ma = matchYouTube.exec(html));) {
|
for (let ma; (ma = matchYouTube.exec(html));) {
|
||||||
let youtubeId = ma[1].match(/^[^&]+/)[0]
|
const youtubeId = ma[1].match(/^[^&]+/)[0]
|
||||||
cache.set(youtubeId, null)
|
cache.set(youtubeId, null)
|
||||||
query.set(entities.decode(ma[1]), youtubeId)
|
query.set(entities.decode(ma[1]), youtubeId)
|
||||||
}
|
}
|
||||||
|
|
||||||
let matchSoundCloud = /<p><a class="embed" href="(https:\/\/soundcloud\.com\/.*?)">.*?<\/a><\/p>/g
|
const matchSoundCloud = /<p><a class="embed" href="(https:\/\/soundcloud\.com\/.*?)">.*?<\/a><\/p>/g
|
||||||
html = await replaceAsync(html, matchSoundCloud, (match, url) => {
|
html = await replaceAsync(html, matchSoundCloud, (match, url) => {
|
||||||
return render(m('.soundcloud.leftalign', [
|
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, ' '))
|
'SoundCloud: ', m('a', { href: entities.decode(url), rel: 'nofollow' }, url.replace('https://soundcloud.com/', '').replace(/[-_]/g, ' ').replace('/', ' - ').replace(/ {2}/g, ' '))
|
||||||
|
@ -104,12 +104,12 @@ export async function cleanMarkup (html) {
|
||||||
|
|
||||||
function replaceYouTube (match, queryString) {
|
function replaceYouTube (match, queryString) {
|
||||||
queryString = entities.decode(queryString)
|
queryString = entities.decode(queryString)
|
||||||
let youtubeId = query.get(queryString)
|
const youtubeId = query.get(queryString)
|
||||||
let thumbnail = 'https://img.youtube.com/vi/' + youtubeId + '/hqdefault.jpg'
|
let thumbnail = 'https://img.youtube.com/vi/' + youtubeId + '/hqdefault.jpg'
|
||||||
let youtubeUrl = 'https://youtube.com/watch?v=' + queryString
|
const youtubeUrl = 'https://youtube.com/watch?v=' + queryString
|
||||||
let title = 'Youtube Video'
|
let title = 'Youtube Video'
|
||||||
let caption = ''
|
let caption = ''
|
||||||
let data = cache.get(youtubeId)
|
const data = cache.get(youtubeId)
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
thumbnail = (data.thumbnails.standard || data.thumbnails.high || data.thumbnails.medium || data.thumbnails.default).url
|
thumbnail = (data.thumbnails.standard || data.thumbnails.high || data.thumbnails.medium || data.thumbnails.default).url
|
||||||
|
@ -138,7 +138,7 @@ export function fixDoubleSpacing (html) {
|
||||||
|
|
||||||
export function fixParagraphIndent (html) {
|
export function fixParagraphIndent (html) {
|
||||||
// from FimFictionConverter by Nyerguds
|
// from FimFictionConverter by Nyerguds
|
||||||
let fixIndent = 2
|
const fixIndent = 2
|
||||||
if (fixIndent > 0) {
|
if (fixIndent > 0) {
|
||||||
// only trigger indenting when finding as many whitespace characters in a row as indicated by the FixIndent setting.
|
// only trigger indenting when finding as many whitespace characters in a row as indicated by the FixIndent setting.
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,6 @@ if (outputStdout) {
|
||||||
const mock = require('mithril/test-utils/browserMock')(global)
|
const mock = require('mithril/test-utils/browserMock')(global)
|
||||||
global.requestAnimationFrame = mock.requestAnimationFrame
|
global.requestAnimationFrame = mock.requestAnimationFrame
|
||||||
|
|
||||||
|
|
||||||
const htmlToText = require('./utils').htmlToText
|
const htmlToText = require('./utils').htmlToText
|
||||||
const FimFic2Epub = require('./FimFic2Epub').default
|
const FimFic2Epub = require('./FimFic2Epub').default
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
|
|
|
@ -10,13 +10,13 @@ export const NS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const tidyOptions = {
|
export const tidyOptions = {
|
||||||
'indent': 'auto',
|
indent: 'auto',
|
||||||
'numeric-entities': 'yes',
|
'numeric-entities': 'yes',
|
||||||
'output-xhtml': 'yes',
|
'output-xhtml': 'yes',
|
||||||
'alt-text': 'Image',
|
'alt-text': 'Image',
|
||||||
'wrap': '0',
|
wrap: '0',
|
||||||
'quiet': 'yes',
|
quiet: 'yes',
|
||||||
'newline': 'LF',
|
newline: 'LF',
|
||||||
'tidy-mark': 'no',
|
'tidy-mark': 'no',
|
||||||
'show-body-only': 'auto'
|
'show-body-only': 'auto'
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import fetch from './fetch'
|
||||||
|
|
||||||
if (typeof safari !== 'undefined') {
|
if (typeof safari !== 'undefined') {
|
||||||
safari.application.addEventListener('message', function (ev) {
|
safari.application.addEventListener('message', function (ev) {
|
||||||
let url = ev.message
|
const url = ev.message
|
||||||
fetch(url, 'arraybuffer').then((buffer) => {
|
fetch(url, 'arraybuffer').then((buffer) => {
|
||||||
console.log('Fetched ' + url)
|
console.log('Fetched ' + url)
|
||||||
ev.target.page.dispatchMessage('remote', {
|
ev.target.page.dispatchMessage('remote', {
|
||||||
|
@ -14,12 +14,12 @@ if (typeof safari !== 'undefined') {
|
||||||
})
|
})
|
||||||
}, false)
|
}, false)
|
||||||
} else {
|
} else {
|
||||||
let onMessage = chrome.extension.onMessage ? chrome.extension.onMessage : chrome.runtime.onMessage
|
const onMessage = chrome.extension.onMessage ? chrome.extension.onMessage : chrome.runtime.onMessage
|
||||||
|
|
||||||
onMessage.addListener(function (request, sender, sendResponse) {
|
onMessage.addListener(function (request, sender, sendResponse) {
|
||||||
if (typeof request === 'string') {
|
if (typeof request === 'string') {
|
||||||
fetch(request, 'blob').then((blob) => {
|
fetch(request, 'blob').then((blob) => {
|
||||||
let ourl = URL.createObjectURL(blob)
|
const ourl = URL.createObjectURL(blob)
|
||||||
console.log('Fetched', request)
|
console.log('Fetched', request)
|
||||||
sendResponse(ourl)
|
sendResponse(ourl)
|
||||||
})
|
})
|
||||||
|
|
10
src/fetch.js
10
src/fetch.js
|
@ -11,9 +11,9 @@ function fetchNode (url, responseType) {
|
||||||
url: url,
|
url: url,
|
||||||
encoding: responseType ? null : 'utf8',
|
encoding: responseType ? null : 'utf8',
|
||||||
headers: {
|
headers: {
|
||||||
'referer': 'https://fimfiction.net/',
|
referer: 'https://fimfiction.net/',
|
||||||
'cookie': 'view_mature=true',
|
cookie: 'view_mature=true',
|
||||||
'accept': '*/*'
|
accept: '*/*'
|
||||||
}
|
}
|
||||||
}, (error, response, body) => {
|
}, (error, response, body) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
|
@ -46,7 +46,7 @@ export default function fetch (url, responseType) {
|
||||||
cache: 'default',
|
cache: 'default',
|
||||||
redirect: 'follow',
|
redirect: 'follow',
|
||||||
headers: {
|
headers: {
|
||||||
'accept': '*/*' // Fix for not getting webp images from Fimfiction
|
accept: '*/*' // Fix for not getting webp images from Fimfiction
|
||||||
},
|
},
|
||||||
referrer: window.location.origin
|
referrer: window.location.origin
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
|
@ -61,7 +61,7 @@ export default function fetch (url, responseType) {
|
||||||
reject(new Error('Error fetching ' + url + ' (' + err + ')'))
|
reject(new Error('Error fetching ' + url + ' (' + err + ')'))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
let x = new XMLHttpRequest()
|
const x = new XMLHttpRequest()
|
||||||
x.withCredentials = true
|
x.withCredentials = true
|
||||||
x.setRequestHeader('accept', '*/*') // Fix for not getting webp images from Fimfiction
|
x.setRequestHeader('accept', '*/*') // Fix for not getting webp images from Fimfiction
|
||||||
x.open('get', url, true)
|
x.open('get', url, true)
|
||||||
|
|
|
@ -7,25 +7,25 @@ const safariQueue = {}
|
||||||
|
|
||||||
// messaging with the safari extension global page
|
// messaging with the safari extension global page
|
||||||
function safariHandler (ev) {
|
function safariHandler (ev) {
|
||||||
let type = ev.message.type
|
const type = ev.message.type
|
||||||
let url = ev.message.input
|
const url = ev.message.input
|
||||||
let data = ev.message.output // arraybuffer
|
const data = ev.message.output // arraybuffer
|
||||||
if (!safariQueue[url]) {
|
if (!safariQueue[url]) {
|
||||||
// console.error("Unable to get callback for " + url, JSON.stringify(safariQueue))
|
// console.error("Unable to get callback for " + url, JSON.stringify(safariQueue))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let cb = safariQueue[url].cb
|
const cb = safariQueue[url].cb
|
||||||
let responseType = safariQueue[url].responseType
|
const responseType = safariQueue[url].responseType
|
||||||
console.log(url, cb, responseType, data)
|
console.log(url, cb, responseType, data)
|
||||||
delete safariQueue[url]
|
delete safariQueue[url]
|
||||||
|
|
||||||
if (responseType === 'blob') {
|
if (responseType === 'blob') {
|
||||||
let blob = new Blob([data], { type: type })
|
const blob = new Blob([data], { type: type })
|
||||||
cb(blob, type)
|
cb(blob, type)
|
||||||
} else {
|
} else {
|
||||||
if (!responseType) {
|
if (!responseType) {
|
||||||
let blob = new Blob([data], { type: type })
|
const blob = new Blob([data], { type: type })
|
||||||
let fr = new FileReader()
|
const fr = new FileReader()
|
||||||
fr.onloadend = function () {
|
fr.onloadend = function () {
|
||||||
cb(fr.result, type)
|
cb(fr.result, type)
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ function textToSentences (text) {
|
||||||
.split(/\s*\0/)
|
.split(/\s*\0/)
|
||||||
|
|
||||||
for (let i = 0; i < tokenSentences.length; i++) {
|
for (let i = 0; i < tokenSentences.length; i++) {
|
||||||
let s = tokenSentences[i]
|
const s = tokenSentences[i]
|
||||||
if (s.trim().length === 0) {
|
if (s.trim().length === 0) {
|
||||||
if (i - 1 >= 0) tokenSentences[i - 1] += s
|
if (i - 1 >= 0) tokenSentences[i - 1] += s
|
||||||
tokenSentences.splice(i, 1)
|
tokenSentences.splice(i, 1)
|
||||||
|
@ -61,15 +61,15 @@ function textToSentences (text) {
|
||||||
function fixupTree (node, parent) {
|
function fixupTree (node, parent) {
|
||||||
if (node.tag !== '#') {
|
if (node.tag !== '#') {
|
||||||
if (node.text && !node.tag.match(specialTags)) {
|
if (node.text && !node.tag.match(specialTags)) {
|
||||||
let el = et.Element('#')
|
const el = et.Element('#')
|
||||||
el.text = node.text
|
el.text = node.text
|
||||||
node._children.unshift(el)
|
node._children.unshift(el)
|
||||||
delete node.text
|
delete node.text
|
||||||
}
|
}
|
||||||
if (node.tail) {
|
if (node.tail) {
|
||||||
let el = et.Element('#')
|
const el = et.Element('#')
|
||||||
el.text = node.tail
|
el.text = node.tail
|
||||||
let pos = parent._children.indexOf(node) + 1
|
const pos = parent._children.indexOf(node) + 1
|
||||||
parent._children.splice(pos, 0, el)
|
parent._children.splice(pos, 0, el)
|
||||||
delete node.tail
|
delete node.tail
|
||||||
}
|
}
|
||||||
|
@ -84,11 +84,11 @@ function addSpansToNode (node, parent, state) {
|
||||||
if (node.tag === '#') {
|
if (node.tag === '#') {
|
||||||
state.segment++
|
state.segment++
|
||||||
|
|
||||||
let sentences = textToSentences(node.text)
|
const sentences = textToSentences(node.text)
|
||||||
let pos
|
let pos
|
||||||
|
|
||||||
sentences.forEach((sentence) => {
|
sentences.forEach((sentence) => {
|
||||||
let span = createSpan(state.paragraph, state.segment++)
|
const span = createSpan(state.paragraph, state.segment++)
|
||||||
span.text = sentence
|
span.text = sentence
|
||||||
|
|
||||||
// insert the span before the text node
|
// insert the span before the text node
|
||||||
|
|
62
src/main.js
62
src/main.js
|
@ -8,15 +8,15 @@ import { saveAs } from 'file-saver'
|
||||||
import autosize from 'autosize'
|
import autosize from 'autosize'
|
||||||
import { htmlToText } from './utils'
|
import { htmlToText } from './utils'
|
||||||
|
|
||||||
m.withAttr = function(attrName, callback, context) {
|
m.withAttr = function (attrName, callback, context) {
|
||||||
return function(e) {
|
return function (e) {
|
||||||
callback.call(context || this, attrName in e.currentTarget ? e.currentTarget[attrName] : e.currentTarget.getAttribute(attrName))
|
callback.call(context || this, attrName in e.currentTarget ? e.currentTarget[attrName] : e.currentTarget.getAttribute(attrName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function blobToDataURL (blob) {
|
function blobToDataURL (blob) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let fr = new FileReader()
|
const fr = new FileReader()
|
||||||
fr.onloadend = function (e) { resolve(fr.result) }
|
fr.onloadend = function (e) { resolve(fr.result) }
|
||||||
fr.readAsDataURL(blob)
|
fr.readAsDataURL(blob)
|
||||||
})
|
})
|
||||||
|
@ -24,7 +24,7 @@ function blobToDataURL (blob) {
|
||||||
|
|
||||||
function blobToArrayBuffer (blob) {
|
function blobToArrayBuffer (blob) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let fr = new FileReader()
|
const fr = new FileReader()
|
||||||
fr.onloadend = function (e) { resolve(fr.result) }
|
fr.onloadend = function (e) { resolve(fr.result) }
|
||||||
fr.readAsArrayBuffer(blob)
|
fr.readAsArrayBuffer(blob)
|
||||||
})
|
})
|
||||||
|
@ -37,23 +37,23 @@ try {
|
||||||
pageStoryId = document.location.pathname.match(/^\/story\/(\d*)/)[1]
|
pageStoryId = document.location.pathname.match(/^\/story\/(\d*)/)[1]
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
let logoUrl = chrome.extension.getURL('fimfic2epub-logo.png')
|
const logoUrl = chrome.extension.getURL('fimfic2epub-logo.png')
|
||||||
let ffc
|
let ffc
|
||||||
let stories = document.querySelectorAll('.story_container')
|
const stories = document.querySelectorAll('.story_container')
|
||||||
|
|
||||||
stories.forEach((story) => {
|
stories.forEach((story) => {
|
||||||
let id = story.dataset.story
|
const id = story.dataset.story
|
||||||
function epubClick (e) {
|
function epubClick (e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
openStory(id)
|
openStory(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
let epubButtons = story.querySelectorAll('.drop-down ul li a[title="Download Story (.epub)"]')
|
const epubButtons = story.querySelectorAll('.drop-down ul li a[title="Download Story (.epub)"]')
|
||||||
if (epubButtons.length === 0) return
|
if (epubButtons.length === 0) return
|
||||||
for (let i = 0; i < epubButtons.length; i++) {
|
for (let i = 0; i < epubButtons.length; i++) {
|
||||||
epubButtons[i].addEventListener('click', epubClick, false)
|
epubButtons[i].addEventListener('click', epubClick, false)
|
||||||
}
|
}
|
||||||
let logo = new Image()
|
const logo = new Image()
|
||||||
logo.className = 'fimfic2epub-logo'
|
logo.className = 'fimfic2epub-logo'
|
||||||
logo.title = 'Download EPUB with fimfic2epub'
|
logo.title = 'Download EPUB with fimfic2epub'
|
||||||
logo.src = logoUrl
|
logo.src = logoUrl
|
||||||
|
@ -61,17 +61,17 @@ stories.forEach((story) => {
|
||||||
logo.addEventListener('click', epubClick, false)
|
logo.addEventListener('click', epubClick, false)
|
||||||
})
|
})
|
||||||
|
|
||||||
let cards = document.querySelectorAll('.story-card-container')
|
const cards = document.querySelectorAll('.story-card-container')
|
||||||
cards.forEach((card) => {
|
cards.forEach((card) => {
|
||||||
let id
|
let id
|
||||||
let classes = card.className.split(' ')
|
const classes = card.className.split(' ')
|
||||||
for (let i = 0; i < classes.length && !id; i++) {
|
for (let i = 0; i < classes.length && !id; i++) {
|
||||||
let c = classes[i]
|
const c = classes[i]
|
||||||
id = c.substring(21)
|
id = c.substring(21)
|
||||||
}
|
}
|
||||||
if (!id) return
|
if (!id) return
|
||||||
let flip = card.querySelector('a.card-flip')
|
const flip = card.querySelector('a.card-flip')
|
||||||
let epubButton = card.querySelector('a[title="Download .ePub"]')
|
const epubButton = card.querySelector('a[title="Download .ePub"]')
|
||||||
if (!epubButton) return
|
if (!epubButton) return
|
||||||
epubButton.addEventListener('click', function (e) {
|
epubButton.addEventListener('click', function (e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
@ -84,7 +84,7 @@ const dialogContainer = document.createElement('div')
|
||||||
dialogContainer.id = 'epubDialogContainer'
|
dialogContainer.id = 'epubDialogContainer'
|
||||||
document.body.appendChild(dialogContainer)
|
document.body.appendChild(dialogContainer)
|
||||||
|
|
||||||
let checkbox = {
|
const checkbox = {
|
||||||
view: function ({ attrs, children }) {
|
view: function ({ attrs, children }) {
|
||||||
return m('label.toggleable-switch', [
|
return m('label.toggleable-switch', [
|
||||||
m('input', Object.assign({
|
m('input', Object.assign({
|
||||||
|
@ -113,10 +113,10 @@ function redraw (arg) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ffcProgress = prop(0)
|
const ffcProgress = prop(0)
|
||||||
let ffcStatus = prop('')
|
const ffcStatus = prop('')
|
||||||
|
|
||||||
let dialog = {
|
const dialog = {
|
||||||
oninit () {
|
oninit () {
|
||||||
const ctrl = this
|
const ctrl = this
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ let dialog = {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setCoverFile = (e) => {
|
this.setCoverFile = (e) => {
|
||||||
let el = e.dom || e.target
|
const el = e.dom || e.target
|
||||||
if (el.target) {
|
if (el.target) {
|
||||||
this.coverUrl('')
|
this.coverUrl('')
|
||||||
}
|
}
|
||||||
|
@ -182,7 +182,7 @@ let dialog = {
|
||||||
|
|
||||||
this.setSubjects = function () {
|
this.setSubjects = function () {
|
||||||
// 'this' is the textarea
|
// 'this' is the textarea
|
||||||
let set = new Set()
|
const set = new Set()
|
||||||
ctrl.subjects(this.value.split('\n').map((s) => s.trim()).filter((s) => {
|
ctrl.subjects(this.value.split('\n').map((s) => s.trim()).filter((s) => {
|
||||||
if (!s) return false
|
if (!s) return false
|
||||||
if (set.has(s)) return false
|
if (set.has(s)) return false
|
||||||
|
@ -200,16 +200,16 @@ let dialog = {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ondown = (e) => {
|
this.ondown = (e) => {
|
||||||
let rect = this.el().firstChild.getBoundingClientRect()
|
const rect = this.el().firstChild.getBoundingClientRect()
|
||||||
let offset = { x: e.pageX - rect.left - document.documentElement.scrollLeft, y: e.pageY - rect.top - document.documentElement.scrollTop }
|
const offset = { x: e.pageX - rect.left - document.documentElement.scrollLeft, y: e.pageY - rect.top - document.documentElement.scrollTop }
|
||||||
this.dragging(true)
|
this.dragging(true)
|
||||||
let onmove = (e) => {
|
const onmove = (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (this.dragging()) {
|
if (this.dragging()) {
|
||||||
this.move(e.pageX - offset.x, e.pageY - offset.y)
|
this.move(e.pageX - offset.x, e.pageY - offset.y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let onup = () => {
|
const onup = () => {
|
||||||
this.dragging(false)
|
this.dragging(false)
|
||||||
window.removeEventListener('mousemove', onmove)
|
window.removeEventListener('mousemove', onmove)
|
||||||
window.removeEventListener('mouseup', onup)
|
window.removeEventListener('mouseup', onup)
|
||||||
|
@ -219,8 +219,8 @@ let dialog = {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.move = (xpos, ypos) => {
|
this.move = (xpos, ypos) => {
|
||||||
let bc = document.querySelector('.body_container')
|
const bc = document.querySelector('.body_container')
|
||||||
let rect = this.el().firstChild.getBoundingClientRect()
|
const rect = this.el().firstChild.getBoundingClientRect()
|
||||||
this.xpos(Math.max(0, Math.min(xpos, bc.offsetWidth - rect.width)))
|
this.xpos(Math.max(0, Math.min(xpos, bc.offsetWidth - rect.width)))
|
||||||
this.ypos(Math.max(0, Math.min(ypos, bc.offsetHeight - rect.height)))
|
this.ypos(Math.max(0, Math.min(ypos, bc.offsetHeight - rect.height)))
|
||||||
this.el().style.left = this.xpos() + 'px'
|
this.el().style.left = this.xpos() + 'px'
|
||||||
|
@ -228,7 +228,7 @@ let dialog = {
|
||||||
}
|
}
|
||||||
this.center = () => {
|
this.center = () => {
|
||||||
if (this.dragging()) return
|
if (this.dragging()) return
|
||||||
let rect = this.el().firstChild.getBoundingClientRect()
|
const rect = this.el().firstChild.getBoundingClientRect()
|
||||||
this.move(
|
this.move(
|
||||||
Math.max(document.documentElement.scrollLeft, (window.innerWidth / 2) - (rect.width / 2) + document.documentElement.scrollLeft),
|
Math.max(document.documentElement.scrollLeft, (window.innerWidth / 2) - (rect.width / 2) + document.documentElement.scrollLeft),
|
||||||
Math.max(document.documentElement.scrollTop, 100 + document.documentElement.scrollTop)
|
Math.max(document.documentElement.scrollTop, 100 + document.documentElement.scrollTop)
|
||||||
|
@ -242,7 +242,7 @@ let dialog = {
|
||||||
},
|
},
|
||||||
|
|
||||||
view (vnode) {
|
view (vnode) {
|
||||||
let ctrl = vnode.state
|
const ctrl = vnode.state
|
||||||
return m('.drop-down-pop-up-container', { oncreate: ctrl.onOpen.bind(ctrl) }, m('.drop-down-pop-up', { style: { 'min-width': '720px' } }, [
|
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('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', [
|
m('.drop-down-pop-up-content', [
|
||||||
|
@ -295,7 +295,7 @@ let dialog = {
|
||||||
ffcProgress() >= 0 ? m('.rating_container',
|
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(' ') ] : null,
|
ffcProgress() >= 0 && ffcProgress() < 1 ? [m('i.fa.fa-spin.fa-spinner'), m.trust(' ')] : null,
|
||||||
ffcStatus()
|
ffcStatus()
|
||||||
) : null,
|
) : null,
|
||||||
m('div', { style: 'clear: both' })
|
m('div', { style: 'clear: both' })
|
||||||
|
|
|
@ -7,8 +7,8 @@ import fileType from 'file-type'
|
||||||
|
|
||||||
async function subsetFont (fontPath, glyphs, options = {}) {
|
async function subsetFont (fontPath, glyphs, options = {}) {
|
||||||
let data
|
let data
|
||||||
let fontdata = Buffer.from(fontPath, 'binary')
|
const fontdata = Buffer.from(fontPath, 'binary')
|
||||||
let type = fileType(fontdata)
|
const type = fileType(fontdata)
|
||||||
if (type && type.mime === 'font/ttf') {
|
if (type && type.mime === 'font/ttf') {
|
||||||
data = fontdata.buffer
|
data = fontdata.buffer
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -18,7 +18,7 @@ function nth (d) {
|
||||||
|
|
||||||
export function prettyDate (d) {
|
export function prettyDate (d) {
|
||||||
// format: 27th Oct 2011
|
// format: 27th Oct 2011
|
||||||
let months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
|
const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
|
||||||
return d.getDate() + nth(d) + ' ' + months[d.getMonth()].substring(0, 3) + ' ' + d.getFullYear()
|
return d.getDate() + nth(d) + ' ' + months[d.getMonth()].substring(0, 3) + ' ' + d.getFullYear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ function chapterBars (chapters, currentChapter = -1, highlightCurrent = false) {
|
||||||
if (chapters.length <= 1) return null
|
if (chapters.length <= 1) return null
|
||||||
let windowSize = 50
|
let windowSize = 50
|
||||||
let wordCounts = []
|
let wordCounts = []
|
||||||
let highestWordCount = chapters.reduce((max, ch) => {
|
const highestWordCount = chapters.reduce((max, ch) => {
|
||||||
wordCounts.push(ch.realWordCount)
|
wordCounts.push(ch.realWordCount)
|
||||||
if (ch.realWordCount > max) return ch.realWordCount
|
if (ch.realWordCount > max) return ch.realWordCount
|
||||||
return max
|
return max
|
||||||
|
@ -42,9 +42,9 @@ function chapterBars (chapters, currentChapter = -1, highlightCurrent = false) {
|
||||||
currentChapter -= start
|
currentChapter -= start
|
||||||
}
|
}
|
||||||
wordCounts = wordCounts.map((c) => c / highestWordCount)
|
wordCounts = wordCounts.map((c) => c / highestWordCount)
|
||||||
let barWidth = 9
|
const barWidth = 9
|
||||||
let barSpacing = 2
|
const barSpacing = 2
|
||||||
let rowSpacing = 9
|
const rowSpacing = 9
|
||||||
const barCount = Math.min(wordCounts.length, windowSize)
|
const barCount = Math.min(wordCounts.length, windowSize)
|
||||||
const rows = Math.floor(wordCounts.length / barCount) + 1
|
const rows = Math.floor(wordCounts.length / barCount) + 1
|
||||||
const rowHeight = 30 + rowSpacing
|
const rowHeight = 30 + rowSpacing
|
||||||
|
@ -68,9 +68,9 @@ function chapterBars (chapters, currentChapter = -1, highlightCurrent = false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createChapter (ffc, ch, isNotesChapter) {
|
export function createChapter (ffc, ch, isNotesChapter) {
|
||||||
let { content, notes, notesFirst, title, link, linkNotes, index, showHeadings, showDuration, showWordCount } = ch
|
const { content, notes, notesFirst, title, link, linkNotes, index, showHeadings, showDuration, showWordCount } = ch
|
||||||
|
|
||||||
let sections = [
|
const sections = [
|
||||||
m.trust(content || ''),
|
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('p', m('b', 'Author\'s Note:')),
|
||||||
|
@ -124,7 +124,7 @@ export function createChapter (ffc, ch, isNotesChapter) {
|
||||||
function sortSpineItems (items) {
|
function sortSpineItems (items) {
|
||||||
let count = items.length
|
let count = items.length
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
let item = items[i]
|
const item = items[i]
|
||||||
if (!item) {
|
if (!item) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -140,9 +140,9 @@ function sortSpineItems (items) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createOpf (ffc) {
|
export function createOpf (ffc) {
|
||||||
let remotes = []
|
const remotes = []
|
||||||
// let remoteCounter = 0
|
// let remoteCounter = 0
|
||||||
let remoteCache = new Set()
|
const remoteCache = new Set()
|
||||||
ffc.remoteResources.forEach((r, url) => {
|
ffc.remoteResources.forEach((r, url) => {
|
||||||
// remoteCounter++
|
// remoteCounter++
|
||||||
if (!ffc.options.includeExternal) {
|
if (!ffc.options.includeExternal) {
|
||||||
|
@ -176,18 +176,18 @@ export function createOpf (ffc) {
|
||||||
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) =>
|
const 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) =>
|
const 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 = []
|
const manifestNotes = []
|
||||||
let spineNotes = []
|
const spineNotes = []
|
||||||
if (ffc.options.includeAuthorNotes && ffc.options.useAuthorNotesIndex && ffc.hasAuthorNotes) {
|
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) => {
|
ffc.chaptersWithNotes.forEach((num) => {
|
||||||
let id = 'note_' + zeroFill(3, num + 1)
|
const id = 'note_' + zeroFill(3, num + 1)
|
||||||
manifestNotes.push(m('item', { id: id, href: 'Text/' + id + '.xhtml', 'media-type': 'application/xhtml+xml' }))
|
manifestNotes.push(m('item', { id: id, href: 'Text/' + id + '.xhtml', 'media-type': 'application/xhtml+xml' }))
|
||||||
spineNotes.push(m('itemref', { idref: id }))
|
spineNotes.push(m('itemref', { idref: id }))
|
||||||
})
|
})
|
||||||
|
@ -219,8 +219,8 @@ export function createOpf (ffc) {
|
||||||
m('manifest', [
|
m('manifest', [
|
||||||
ffc.coverImage ? m('item', { id: 'cover', href: ffc.coverFilename, 'media-type': ffc.coverType, properties: 'cover-image' }) : 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: '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' : '') }),
|
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.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: 'style', href: 'Styles/style.css', 'media-type': 'text/css' }),
|
||||||
m('item', { id: 'coverstyle', href: 'Styles/coverstyle.css', 'media-type': 'text/css' }),
|
m('item', { id: 'coverstyle', href: 'Styles/coverstyle.css', 'media-type': 'text/css' }),
|
||||||
|
@ -255,7 +255,7 @@ export function createOpf (ffc) {
|
||||||
|
|
||||||
function navPoints (list) {
|
function navPoints (list) {
|
||||||
let playOrder = 1
|
let playOrder = 1
|
||||||
let arr = []
|
const arr = []
|
||||||
for (let i = 0; i < list.length; i++) {
|
for (let i = 0; i < list.length; i++) {
|
||||||
if (!list[i]) continue
|
if (!list[i]) continue
|
||||||
arr.push(m('navPoint', { id: 'navPoint-' + (i + 1), playOrder: playOrder++ }, [
|
arr.push(m('navPoint', { id: 'navPoint-' + (i + 1), playOrder: playOrder++ }, [
|
||||||
|
@ -290,7 +290,7 @@ export function createNcx (ffc) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createNav (ffc) {
|
export function createNav (ffc) {
|
||||||
let list = [
|
const list = [
|
||||||
m('li', m('a', { href: 'Text/cover.xhtml' }, 'Cover')),
|
m('li', m('a', { href: 'Text/cover.xhtml' }, 'Cover')),
|
||||||
m('li', m('a', { href: 'Text/title.xhtml' }, 'Title Page')),
|
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
|
ffc.storyInfo.chapters.length > 1 || (ffc.options.includeAuthorNotes && ffc.options.useAuthorNotesIndex && ffc.hasAuthorNotes) ? m('li', m('a', { href: 'nav.xhtml' }, 'Contents')) : null
|
||||||
|
@ -299,7 +299,7 @@ export function createNav (ffc) {
|
||||||
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) =>
|
const prettyList = ffc.storyInfo.chapters.map((ch, num) =>
|
||||||
m('li.item', [
|
m('li.item', [
|
||||||
m('.floatbox', m('span.wordcount', ch.realWordCount.toLocaleString('en-GB'))),
|
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),
|
||||||
|
@ -334,8 +334,8 @@ export function createNav (ffc) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createNotesNav (ffc) {
|
export function createNotesNav (ffc) {
|
||||||
let list = ffc.chaptersWithNotes.map((num) => {
|
const list = ffc.chaptersWithNotes.map((num) => {
|
||||||
let ch = ffc.storyInfo.chapters[num]
|
const 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))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -362,7 +362,7 @@ export function createNotesNav (ffc) {
|
||||||
export function createCoverPage (ffc) {
|
export function createCoverPage (ffc) {
|
||||||
let body
|
let body
|
||||||
|
|
||||||
let { width, height } = ffc.coverImageDimensions
|
const { width, height } = ffc.coverImageDimensions
|
||||||
|
|
||||||
if (ffc.coverImage) {
|
if (ffc.coverImage) {
|
||||||
body = m('svg#cover', { xmlns: NS.SVG, 'xmlns:xlink': NS.XLINK, version: '1.1', viewBox: '0 0 ' + width + ' ' + height },
|
body = m('svg#cover', { xmlns: NS.SVG, 'xmlns:xlink': NS.XLINK, version: '1.1', viewBox: '0 0 ' + width + ' ' + height },
|
||||||
|
@ -402,7 +402,7 @@ function infoBox (heading, data, title) {
|
||||||
|
|
||||||
function calcReadingTime (ffc, wordCount = 0) {
|
function calcReadingTime (ffc, wordCount = 0) {
|
||||||
const wpm = ffc.options.wordsPerMinute
|
const wpm = ffc.options.wordsPerMinute
|
||||||
let time = (wordCount || ffc.totalWordCount) / wpm
|
const time = (wordCount || ffc.totalWordCount) / wpm
|
||||||
let value = 0
|
let value = 0
|
||||||
let unit = ''
|
let unit = ''
|
||||||
if (time < 1) {
|
if (time < 1) {
|
||||||
|
|
22
src/utils.js
22
src/utils.js
|
@ -9,13 +9,13 @@ import { unicode } from './constants'
|
||||||
export function replaceAsync (str, re, callback) {
|
export function replaceAsync (str, re, callback) {
|
||||||
// http://es5.github.io/#x15.5.4.11
|
// http://es5.github.io/#x15.5.4.11
|
||||||
str = String(str)
|
str = String(str)
|
||||||
let parts = []
|
const parts = []
|
||||||
let i = 0
|
let i = 0
|
||||||
if (Object.prototype.toString.call(re) === '[object RegExp]') {
|
if (Object.prototype.toString.call(re) === '[object RegExp]') {
|
||||||
if (re.global) { re.lastIndex = i }
|
if (re.global) { re.lastIndex = i }
|
||||||
let m
|
let m
|
||||||
while ((m = re.exec(str))) {
|
while ((m = re.exec(str))) {
|
||||||
let args = m.concat([m.index, m.input])
|
const args = m.concat([m.index, m.input])
|
||||||
parts.push(str.slice(i, m.index), callback.apply(null, args))
|
parts.push(str.slice(i, m.index), callback.apply(null, args))
|
||||||
i = re.lastIndex
|
i = re.lastIndex
|
||||||
if (!re.global) { break } // for non-global regexes only take the first match
|
if (!re.global) { break } // for non-global regexes only take the first match
|
||||||
|
@ -45,22 +45,22 @@ export function webp2png (data) {
|
||||||
webpdecoder = new libwebp.WebPDecoder()
|
webpdecoder = new libwebp.WebPDecoder()
|
||||||
}
|
}
|
||||||
|
|
||||||
let frame = WebPRiffParser(data, 0).frames[0]
|
const frame = WebPRiffParser(data, 0).frames[0]
|
||||||
let width = [0]
|
const width = [0]
|
||||||
let height = [0]
|
const height = [0]
|
||||||
let decodedData = webpdecoder.WebPDecodeRGBA(
|
const decodedData = webpdecoder.WebPDecodeRGBA(
|
||||||
data,
|
data,
|
||||||
frame['src_off'], frame['src_size'],
|
frame.src_off, frame.src_size,
|
||||||
width, height
|
width, height
|
||||||
)
|
)
|
||||||
|
|
||||||
let png = new PNGPacker({})
|
const png = new PNGPacker({})
|
||||||
let buffers = []
|
const buffers = []
|
||||||
png.on('data', (chunk) => {
|
png.on('data', (chunk) => {
|
||||||
buffers.push(chunk)
|
buffers.push(chunk)
|
||||||
})
|
})
|
||||||
png.once('end', () => {
|
png.once('end', () => {
|
||||||
let pngData = Buffer.concat(buffers)
|
const pngData = Buffer.concat(buffers)
|
||||||
resolve(pngData)
|
resolve(pngData)
|
||||||
})
|
})
|
||||||
png.pack(decodedData, width[0], height[0])
|
png.pack(decodedData, width[0], height[0])
|
||||||
|
@ -122,7 +122,7 @@ export async function readingEase (text, wakeupInterval = Infinity, progresscb)
|
||||||
let lastTime = Date.now()
|
let lastTime = Date.now()
|
||||||
|
|
||||||
for (let i = 0; i < tokenSentences.length; i++) {
|
for (let i = 0; i < tokenSentences.length; i++) {
|
||||||
let now = Date.now()
|
const now = Date.now()
|
||||||
if (lastTime + wakeupInterval < now) {
|
if (lastTime + wakeupInterval < now) {
|
||||||
lastTime = now
|
lastTime = now
|
||||||
if (typeof progresscb === 'function') {
|
if (typeof progresscb === 'function') {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import nodeExternals from 'webpack-node-externals'
|
import nodeExternals from 'webpack-node-externals'
|
||||||
|
|
||||||
let inProduction = process.env.NODE_ENV === 'production' || process.argv.indexOf('-p') !== -1
|
const inProduction = process.env.NODE_ENV === 'production' || process.argv.indexOf('-p') !== -1
|
||||||
|
|
||||||
const bundleExtensionConfig = {
|
const bundleExtensionConfig = {
|
||||||
entry: {
|
entry: {
|
||||||
|
|
Loading…
Reference in a new issue