This commit is contained in:
daniel-j 2019-10-08 11:31:42 +02:00
parent 19194dea31
commit 9a2e60c1c9
15 changed files with 175 additions and 174 deletions

View file

@ -33,7 +33,7 @@ if (isStandalone) {
webpackConfig.pop()
}
let watchOpts = {
const watchOpts = {
readDelay: 500,
verbose: true,
read: false
@ -41,12 +41,12 @@ let watchOpts = {
let packageVersion = require('./package.json').version
let webpackDefines = new webpack.DefinePlugin({
const webpackDefines = new webpack.DefinePlugin({
FIMFIC2EPUB_VERSION: JSON.stringify(packageVersion)
})
// 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) => {
c.plugins.push(webpackDefines)
@ -89,8 +89,8 @@ function webpackTask () {
}
function convertFontAwesomeVars (contents) {
let vars = {}
let matchVar = /\$fa-var-(.*?): "\\(.*?)";/g
const vars = {}
const matchVar = /\$fa-var-(.*?): "\\(.*?)";/g
let ma
for (;(ma = matchVar.exec(contents));) {
vars[ma[1]] = String.fromCharCode(parseInt(ma[2], 16))

View file

@ -9,7 +9,7 @@
"url": "https://github.com/daniel-j/fimfic2epub.git"
},
"scripts": {
"build": "NODE_ENV=production gulp",
"build": "gulp",
"dev": "NODE_ENV=development gulp"
},
"bin": {

View file

@ -31,9 +31,9 @@ const trimWhitespace = /^\s*(<br\s*\/?\s*>)+|(<br\s*\/?\s*>)+\s*$/ig
class FimFic2Epub extends EventEmitter {
static getStoryId (id) {
if (isNaN(id)) {
let url = URL(id)
const url = URL(id)
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) {
id = m[1]
}
@ -49,7 +49,7 @@ class FimFic2Epub extends EventEmitter {
static fetchStoryInfo (storyId, raw = false) {
return new Promise((resolve, reject) => {
storyId = FimFic2Epub.getStoryId(storyId)
let url = '/api/story.php?story=' + storyId
const url = '/api/story.php?story=' + storyId
fetch(url).then((content) => {
let data
try {
@ -63,7 +63,7 @@ class FimFic2Epub extends EventEmitter {
reject(new Error(data.error + ' (id: ' + storyId + ')'))
return
}
let story = data.story
const story = data.story
if (raw) {
resolve(story)
return
@ -223,20 +223,20 @@ class FimFic2Epub extends EventEmitter {
this.progress(0, 0, 'Fetching chapters...')
let chapterCount = this.storyInfo.chapters.length
let url = 'https://fimfiction.net/story/download/' + this.storyInfo.id + '/html'
const chapterCount = this.storyInfo.chapters.length
const url = 'https://fimfiction.net/story/download/' + this.storyInfo.id + '/html'
this.pcache.chapters = fetch(url).then((html) => {
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++) {
const ch = this.storyInfo.chapters[i]
let chapterContent = ma[1]
chapterContent = chapterContent.replace(/<footer>[\s\S]*?<\/footer>/g, '').trim()
let authorNotesPos = chapterContent.indexOf('<aside ')
const authorNotesPos = chapterContent.indexOf('<aside ')
let notesContent = ''
let notesFirst = authorNotesPos === 0
const notesFirst = authorNotesPos === 0
if (authorNotesPos !== -1) {
chapterContent = chapterContent.replace(/<aside class="authors-note">([\s\S]*?)<\/aside>/, (match, content, pos) => {
content = content.replace(/<header><h1>.*?<\/h1><\/header>/, '')
@ -290,14 +290,14 @@ class FimFic2Epub extends EventEmitter {
return Promise.resolve()
}
let checksums = new Map()
const checksums = new Map()
this.progress(0, 0, 'Fetching remote files...')
this.pcache.remoteResources = new Promise((resolve, reject) => {
let iter = this.remoteResources.entries()
const iter = this.remoteResources.entries()
let completeCount = 0
let next = (r) => {
const next = (r) => {
completeCount++
if (r.data) {
this.progress(0, completeCount / this.remoteResources.size, 'Fetched remote file ' + completeCount + ' / ' + this.remoteResources.size)
@ -307,7 +307,7 @@ class FimFic2Epub extends EventEmitter {
recursive()
}
let recursive = () => {
const recursive = () => {
let r = iter.next().value
if (!r) {
if (completeCount === this.remoteResources.size) {
@ -315,7 +315,7 @@ class FimFic2Epub extends EventEmitter {
}
return
}
let url = r[0]
const url = r[0]
r = r[1]
if (r.data) {
next(r)
@ -335,9 +335,9 @@ class FimFic2Epub extends EventEmitter {
}
}
if (info) {
let checksum = crc32(isNode ? data : new Uint8Array(data))
const checksum = crc32(isNode ? data : new Uint8Array(data))
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.filename = sameFile.dest
r.type = sameFile.type
@ -348,11 +348,11 @@ class FimFic2Epub extends EventEmitter {
data = await utils.webp2png(isNode ? data : new Uint8Array(data))
info = fileType(data)
}
let type = info.mime
const type = info.mime
r.type = type
let isImage = type.startsWith('image/')
let folder = isImage ? 'Images' : 'Misc'
let dest = folder + '/*.' + info.ext
const isImage = type.startsWith('image/')
const folder = isImage ? 'Images' : 'Misc'
const dest = folder + '/*.' + info.ext
r.dest = dest.replace('*', r.filename)
r.data = data
}
@ -393,8 +393,8 @@ class FimFic2Epub extends EventEmitter {
this.notesHtml.length = 0
for (let i = 0; i < this.chapters.length; i++) {
let ch = this.storyInfo.chapters[i]
let chapter = this.chapters[i]
const ch = this.storyInfo.chapters[i]
const chapter = this.chapters[i]
let content = chapter.content
if (this.options.typogrify) {
@ -486,8 +486,8 @@ class FimFic2Epub extends EventEmitter {
this.zip.file('OEBPS/Styles/navstyle.css', Buffer.from(navstyleCss, 'utf8'))
for (let i = 0; i < this.chapters.length; i++) {
let filename = 'OEBPS/Text/chapter_' + zeroFill(3, i + 1) + '.xhtml'
let html = this.chaptersHtml[i]
const filename = 'OEBPS/Text/chapter_' + zeroFill(3, i + 1) + '.xhtml'
const html = this.chaptersHtml[i]
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++) {
if (!this.chapters[i].notes) continue
let filename = 'OEBPS/Text/note_' + zeroFill(3, i + 1) + '.xhtml'
let html = this.notesHtml[i]
const filename = 'OEBPS/Text/note_' + zeroFill(3, i + 1) + '.xhtml'
const html = this.notesHtml[i]
this.zip.file(filename, Buffer.from(html, 'utf8'))
}
}
@ -511,7 +511,7 @@ class FimFic2Epub extends EventEmitter {
(paragraphsCss[this.options.paragraphStyle] || '')
, 'utf8'))
let remoteDestCache = new Set()
const remoteDestCache = new Set()
this.remoteResources.forEach((r) => {
if (r.dest && !remoteDestCache.has(r.dest)) {
this.zip.file('OEBPS/' + r.dest, r.data)
@ -541,7 +541,7 @@ class FimFic2Epub extends EventEmitter {
compression: 'DEFLATE',
compressionOptions: { level: 9 }
}, (metadata) => { // onUpdate
let currentPercent = Math.round(metadata.percent / 10) * 10
const currentPercent = Math.round(metadata.percent / 10) * 10
if (lastPercent !== currentPercent) {
lastPercent = currentPercent
this.progress(0, currentPercent / 100, 'Compressing...')
@ -575,7 +575,7 @@ class FimFic2Epub extends EventEmitter {
compressionOptions: { level: 9 }
}, (metadata) => {
if (onUpdate) onUpdate(metadata)
let currentPercent = Math.round(metadata.percent / 20) * 20
const currentPercent = Math.round(metadata.percent / 20) * 20
if (lastPercent !== currentPercent) {
lastPercent = currentPercent
this.progress(0, currentPercent / 100, 'Compressing...')
@ -587,13 +587,15 @@ class FimFic2Epub extends EventEmitter {
this.storyInfo.title = title.trim()
this.filename = FimFic2Epub.getFilename(this.storyInfo)
}
setAuthorName (name) {
this.storyInfo.author.name = name.trim()
this.filename = FimFic2Epub.getFilename(this.storyInfo)
}
setCoverImage (buffer) {
buffer = isNode ? buffer : Buffer.from(new Uint8Array(buffer))
let info = fileType(buffer)
const info = fileType(buffer)
if (!info || !info.mime.startsWith('image/')) {
throw new Error('Invalid image')
}
@ -620,21 +622,21 @@ class FimFic2Epub extends EventEmitter {
findRemoteResources (prefix, where, html) {
let remoteCounter = 1
let matchUrl = /<img.*?src="([^">]*\/([^">]*?))".*?>/g
let emoticonUrl = /static\.fimfiction\.net\/images\/emoticons\/([a-z_]*)\.[a-z]*$/
const matchUrl = /<img.*?src="([^">]*\/([^">]*?))".*?>/g
const emoticonUrl = /static\.fimfiction\.net\/images\/emoticons\/([a-z_]*)\.[a-z]*$/
for (let ma; (ma = matchUrl.exec(html));) {
let url = ma[1]
let cleanurl = entities.decode(url)
const url = ma[1]
const cleanurl = entities.decode(url)
if (this.remoteResources.has(cleanurl)) {
let r = this.remoteResources.get(cleanurl)
const r = this.remoteResources.get(cleanurl)
if (r.where.indexOf(where) === -1) {
r.where.push(where)
}
continue
}
let filename = prefix + '_' + remoteCounter
let emoticon = url.match(emoticonUrl)
const emoticon = url.match(emoticonUrl)
if (emoticon) {
filename = 'emoticon_' + emoticon[1]
}
@ -644,7 +646,7 @@ class FimFic2Epub extends EventEmitter {
}
async findIcons () {
let matchIcon = /<i class="fa fa-fw fa-(.*?)"/g
const matchIcon = /<i class="fa fa-fw fa-(.*?)"/g
this.usedIcons.clear()
const scan = (html) => {
@ -667,10 +669,10 @@ class FimFic2Epub extends EventEmitter {
return
}
let glyphs = [...this.usedIcons].map((name) => {
const glyphs = [...this.usedIcons].map((name) => {
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 })
}
@ -692,7 +694,7 @@ class FimFic2Epub extends EventEmitter {
return Promise.resolve(this.coverImage)
}
this.coverImage = null
let url = this.coverUrl || this.storyInfo.full_image
const url = this.coverUrl || this.storyInfo.full_image
if (!url) {
console.warn('Story has no image. Generating one...')
let canvas
@ -703,7 +705,7 @@ class FimFic2Epub extends EventEmitter {
canvas.width = 1080
canvas.height = 1440
}
let ctx = canvas.getContext('2d')
const ctx = canvas.getContext('2d')
ctx.fillStyle = 'white'
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(12, 12, canvas.width - 24, canvas.height - 24)
let title = this.storyInfo.title
let author = this.storyInfo.author.name
const title = this.storyInfo.title
const author = this.storyInfo.author.name
let fontSize = 150
let width
@ -744,14 +746,14 @@ class FimFic2Epub extends EventEmitter {
this.pcache.coverImage = fetchRemote(url, 'arraybuffer').then((data) => {
data = isNode ? data : new Uint8Array(data)
let info = fileType(data)
const info = fileType(data)
if (info) {
let type = info.mime
let isImage = type.startsWith('image/')
const type = info.mime
const isImage = type.startsWith('image/')
if (!isImage) {
return null
}
let filename = 'Images/cover.' + info.ext
const filename = 'Images/cover.' + info.ext
this.coverFilename = filename
this.coverType = type
@ -771,7 +773,7 @@ class FimFic2Epub extends EventEmitter {
fetchTitlePage () {
let viewMature = true
let isStoryMature = this.storyInfo.content_rating === 2
const isStoryMature = this.storyInfo.content_rating === 2
if (!isNode) {
viewMature = document.cookie.split('; ').includes('view_mature=true')
if (!viewMature && isStoryMature) {
@ -796,19 +798,19 @@ class FimFic2Epub extends EventEmitter {
startTagsPos += html.substring(startTagsPos).indexOf('<ul class="story-tags">') + 23
let tagsHtml = html.substring(startTagsPos)
let endTagsPos = tagsHtml.indexOf('</ul>')
const endTagsPos = tagsHtml.indexOf('</ul>')
tagsHtml = tagsHtml.substring(0, endTagsPos)
let tags = []
const tags = []
let c
tags.byImage = {}
this.subjects.length = 0
this.subjects.push('Fimfiction')
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));) {
let cat = {
const cat = {
url: 'https://fimfiction.net' + c[1],
className: 'story-tag ' + c[2],
name: entities.decode(c[4]),
@ -831,12 +833,12 @@ class FimFic2Epub extends EventEmitter {
html = html.substring(html.indexOf('<hr />') + 6)
}
let endDescPos = html.indexOf('</span>\n')
let description = html.substring(0, endDescPos).trim()
const endDescPos = html.indexOf('</span>\n')
const description = html.substring(0, endDescPos).trim()
this.description = description
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)
ma = html.match(/<span class="approved-date">.*?data-time="(.*?)".*?<\/span>/)
@ -848,7 +850,7 @@ class FimFic2Epub extends EventEmitter {
}
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 authorNotes = ''
@ -860,10 +862,10 @@ class FimFic2Epub extends EventEmitter {
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 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()
@ -877,9 +879,9 @@ class FimFic2Epub extends EventEmitter {
if (!this.options.includeExternal) {
this.remoteResources.forEach((r, url) => {
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++) {
let w = r.where[i]
const w = r.where[i]
if (typeof w === 'number') {
if (ourl.test(this.chapters[w])) {
this.storyInfo.chapters[w].remote = true
@ -895,10 +897,10 @@ class FimFic2Epub extends EventEmitter {
} else {
this.remoteResources.forEach((r, url) => {
if (r.dest && r.originalUrl && r.where) {
let dest = '../' + r.dest
let ourl = new RegExp(escapeStringRegexp(r.originalUrl), 'g')
const dest = '../' + r.dest
const ourl = new RegExp(escapeStringRegexp(r.originalUrl), 'g')
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]) {
this.chaptersHtml[w.chapter] = this.chaptersHtml[w.chapter].replace(ourl, dest)
} else if (typeof w === 'object' && w.note !== undefined && this.notesHtml[w.note]) {

View file

@ -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">')
// 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 }))
// Fix links pointing to pages on fimfiction
// 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) => {
if (url.substring(0, 1) !== '#' && url.substring(0, 2) !== '//' && url.substring(0, 4) !== 'http' && url.substring(0, 1) === '/') {
url = 'https://fimfiction.net' + url
@ -62,14 +62,14 @@ export async function cleanMarkup (html) {
const query = new Map()
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));) {
let youtubeId = ma[1].match(/^[^&]+/)[0]
const youtubeId = ma[1].match(/^[^&]+/)[0]
cache.set(youtubeId, null)
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) => {
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, ' '))
@ -104,12 +104,12 @@ export async function cleanMarkup (html) {
function replaceYouTube (match, 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 youtubeUrl = 'https://youtube.com/watch?v=' + queryString
const youtubeUrl = 'https://youtube.com/watch?v=' + queryString
let title = 'Youtube Video'
let caption = ''
let data = cache.get(youtubeId)
const data = cache.get(youtubeId)
if (data) {
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) {
// from FimFictionConverter by Nyerguds
let fixIndent = 2
const fixIndent = 2
if (fixIndent > 0) {
// only trigger indenting when finding as many whitespace characters in a row as indicated by the FixIndent setting.

View file

@ -39,7 +39,6 @@ if (outputStdout) {
const mock = require('mithril/test-utils/browserMock')(global)
global.requestAnimationFrame = mock.requestAnimationFrame
const htmlToText = require('./utils').htmlToText
const FimFic2Epub = require('./FimFic2Epub').default
const fs = require('fs')

View file

@ -10,13 +10,13 @@ export const NS = {
}
export const tidyOptions = {
'indent': 'auto',
indent: 'auto',
'numeric-entities': 'yes',
'output-xhtml': 'yes',
'alt-text': 'Image',
'wrap': '0',
'quiet': 'yes',
'newline': 'LF',
wrap: '0',
quiet: 'yes',
newline: 'LF',
'tidy-mark': 'no',
'show-body-only': 'auto'
}

View file

@ -4,7 +4,7 @@ import fetch from './fetch'
if (typeof safari !== 'undefined') {
safari.application.addEventListener('message', function (ev) {
let url = ev.message
const url = ev.message
fetch(url, 'arraybuffer').then((buffer) => {
console.log('Fetched ' + url)
ev.target.page.dispatchMessage('remote', {
@ -14,12 +14,12 @@ if (typeof safari !== 'undefined') {
})
}, false)
} 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) {
if (typeof request === 'string') {
fetch(request, 'blob').then((blob) => {
let ourl = URL.createObjectURL(blob)
const ourl = URL.createObjectURL(blob)
console.log('Fetched', request)
sendResponse(ourl)
})

View file

@ -11,9 +11,9 @@ function fetchNode (url, responseType) {
url: url,
encoding: responseType ? null : 'utf8',
headers: {
'referer': 'https://fimfiction.net/',
'cookie': 'view_mature=true',
'accept': '*/*'
referer: 'https://fimfiction.net/',
cookie: 'view_mature=true',
accept: '*/*'
}
}, (error, response, body) => {
if (error) {
@ -46,7 +46,7 @@ export default function fetch (url, responseType) {
cache: 'default',
redirect: 'follow',
headers: {
'accept': '*/*' // Fix for not getting webp images from Fimfiction
accept: '*/*' // Fix for not getting webp images from Fimfiction
},
referrer: window.location.origin
}).then((response) => {
@ -61,7 +61,7 @@ export default function fetch (url, responseType) {
reject(new Error('Error fetching ' + url + ' (' + err + ')'))
})
} else {
let x = new XMLHttpRequest()
const x = new XMLHttpRequest()
x.withCredentials = true
x.setRequestHeader('accept', '*/*') // Fix for not getting webp images from Fimfiction
x.open('get', url, true)

View file

@ -7,25 +7,25 @@ const safariQueue = {}
// messaging with the safari extension global page
function safariHandler (ev) {
let type = ev.message.type
let url = ev.message.input
let data = ev.message.output // arraybuffer
const type = ev.message.type
const url = ev.message.input
const data = ev.message.output // arraybuffer
if (!safariQueue[url]) {
// console.error("Unable to get callback for " + url, JSON.stringify(safariQueue))
return
}
let cb = safariQueue[url].cb
let responseType = safariQueue[url].responseType
const cb = safariQueue[url].cb
const responseType = safariQueue[url].responseType
console.log(url, cb, responseType, data)
delete safariQueue[url]
if (responseType === 'blob') {
let blob = new Blob([data], { type: type })
const blob = new Blob([data], { type: type })
cb(blob, type)
} else {
if (!responseType) {
let blob = new Blob([data], { type: type })
let fr = new FileReader()
const blob = new Blob([data], { type: type })
const fr = new FileReader()
fr.onloadend = function () {
cb(fr.result, type)
}

View file

@ -46,7 +46,7 @@ function textToSentences (text) {
.split(/\s*\0/)
for (let i = 0; i < tokenSentences.length; i++) {
let s = tokenSentences[i]
const s = tokenSentences[i]
if (s.trim().length === 0) {
if (i - 1 >= 0) tokenSentences[i - 1] += s
tokenSentences.splice(i, 1)
@ -61,15 +61,15 @@ function textToSentences (text) {
function fixupTree (node, parent) {
if (node.tag !== '#') {
if (node.text && !node.tag.match(specialTags)) {
let el = et.Element('#')
const el = et.Element('#')
el.text = node.text
node._children.unshift(el)
delete node.text
}
if (node.tail) {
let el = et.Element('#')
const el = et.Element('#')
el.text = node.tail
let pos = parent._children.indexOf(node) + 1
const pos = parent._children.indexOf(node) + 1
parent._children.splice(pos, 0, el)
delete node.tail
}
@ -84,11 +84,11 @@ function addSpansToNode (node, parent, state) {
if (node.tag === '#') {
state.segment++
let sentences = textToSentences(node.text)
const sentences = textToSentences(node.text)
let pos
sentences.forEach((sentence) => {
let span = createSpan(state.paragraph, state.segment++)
const span = createSpan(state.paragraph, state.segment++)
span.text = sentence
// insert the span before the text node

View file

@ -8,15 +8,15 @@ import { saveAs } from 'file-saver'
import autosize from 'autosize'
import { htmlToText } from './utils'
m.withAttr = function(attrName, callback, context) {
return function(e) {
callback.call(context || this, attrName in e.currentTarget ? e.currentTarget[attrName] : e.currentTarget.getAttribute(attrName))
}
m.withAttr = function (attrName, callback, context) {
return function (e) {
callback.call(context || this, attrName in e.currentTarget ? e.currentTarget[attrName] : e.currentTarget.getAttribute(attrName))
}
}
function blobToDataURL (blob) {
return new Promise((resolve, reject) => {
let fr = new FileReader()
const fr = new FileReader()
fr.onloadend = function (e) { resolve(fr.result) }
fr.readAsDataURL(blob)
})
@ -24,7 +24,7 @@ function blobToDataURL (blob) {
function blobToArrayBuffer (blob) {
return new Promise((resolve, reject) => {
let fr = new FileReader()
const fr = new FileReader()
fr.onloadend = function (e) { resolve(fr.result) }
fr.readAsArrayBuffer(blob)
})
@ -37,23 +37,23 @@ try {
pageStoryId = document.location.pathname.match(/^\/story\/(\d*)/)[1]
} catch (e) {}
let logoUrl = chrome.extension.getURL('fimfic2epub-logo.png')
const logoUrl = chrome.extension.getURL('fimfic2epub-logo.png')
let ffc
let stories = document.querySelectorAll('.story_container')
const stories = document.querySelectorAll('.story_container')
stories.forEach((story) => {
let id = story.dataset.story
const id = story.dataset.story
function epubClick (e) {
e.preventDefault()
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
for (let i = 0; i < epubButtons.length; i++) {
epubButtons[i].addEventListener('click', epubClick, false)
}
let logo = new Image()
const logo = new Image()
logo.className = 'fimfic2epub-logo'
logo.title = 'Download EPUB with fimfic2epub'
logo.src = logoUrl
@ -61,17 +61,17 @@ stories.forEach((story) => {
logo.addEventListener('click', epubClick, false)
})
let cards = document.querySelectorAll('.story-card-container')
const cards = document.querySelectorAll('.story-card-container')
cards.forEach((card) => {
let id
let classes = card.className.split(' ')
const classes = card.className.split(' ')
for (let i = 0; i < classes.length && !id; i++) {
let c = classes[i]
const c = classes[i]
id = c.substring(21)
}
if (!id) return
let flip = card.querySelector('a.card-flip')
let epubButton = card.querySelector('a[title="Download .ePub"]')
const flip = card.querySelector('a.card-flip')
const epubButton = card.querySelector('a[title="Download .ePub"]')
if (!epubButton) return
epubButton.addEventListener('click', function (e) {
e.preventDefault()
@ -84,7 +84,7 @@ const dialogContainer = document.createElement('div')
dialogContainer.id = 'epubDialogContainer'
document.body.appendChild(dialogContainer)
let checkbox = {
const checkbox = {
view: function ({ attrs, children }) {
return m('label.toggleable-switch', [
m('input', Object.assign({
@ -113,10 +113,10 @@ function redraw (arg) {
}
}
let ffcProgress = prop(0)
let ffcStatus = prop('')
const ffcProgress = prop(0)
const ffcStatus = prop('')
let dialog = {
const dialog = {
oninit () {
const ctrl = this
@ -173,7 +173,7 @@ let dialog = {
}
this.setCoverFile = (e) => {
let el = e.dom || e.target
const el = e.dom || e.target
if (el.target) {
this.coverUrl('')
}
@ -182,7 +182,7 @@ let dialog = {
this.setSubjects = function () {
// 'this' is the textarea
let set = new Set()
const set = new Set()
ctrl.subjects(this.value.split('\n').map((s) => s.trim()).filter((s) => {
if (!s) return false
if (set.has(s)) return false
@ -200,16 +200,16 @@ 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 }
const rect = this.el().firstChild.getBoundingClientRect()
const offset = { x: e.pageX - rect.left - document.documentElement.scrollLeft, y: e.pageY - rect.top - document.documentElement.scrollTop }
this.dragging(true)
let onmove = (e) => {
const onmove = (e) => {
e.preventDefault()
if (this.dragging()) {
this.move(e.pageX - offset.x, e.pageY - offset.y)
}
}
let onup = () => {
const onup = () => {
this.dragging(false)
window.removeEventListener('mousemove', onmove)
window.removeEventListener('mouseup', onup)
@ -219,8 +219,8 @@ let dialog = {
}
this.move = (xpos, ypos) => {
let bc = document.querySelector('.body_container')
let rect = this.el().firstChild.getBoundingClientRect()
const bc = document.querySelector('.body_container')
const rect = this.el().firstChild.getBoundingClientRect()
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.el().style.left = this.xpos() + 'px'
@ -228,7 +228,7 @@ let dialog = {
}
this.center = () => {
if (this.dragging()) return
let rect = this.el().firstChild.getBoundingClientRect()
const rect = this.el().firstChild.getBoundingClientRect()
this.move(
Math.max(document.documentElement.scrollLeft, (window.innerWidth / 2) - (rect.width / 2) + document.documentElement.scrollLeft),
Math.max(document.documentElement.scrollTop, 100 + document.documentElement.scrollTop)
@ -242,7 +242,7 @@ let dialog = {
},
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' } }, [
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', [
@ -295,7 +295,7 @@ let dialog = {
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 + '%' } })),
' ',
ffcProgress() >= 0 && ffcProgress() < 1 ? [ m('i.fa.fa-spin.fa-spinner'), m.trust('&nbsp;&nbsp;') ] : null,
ffcProgress() >= 0 && ffcProgress() < 1 ? [m('i.fa.fa-spin.fa-spinner'), m.trust('&nbsp;&nbsp;')] : null,
ffcStatus()
) : null,
m('div', { style: 'clear: both' })

View file

@ -7,8 +7,8 @@ import fileType from 'file-type'
async function subsetFont (fontPath, glyphs, options = {}) {
let data
let fontdata = Buffer.from(fontPath, 'binary')
let type = fileType(fontdata)
const fontdata = Buffer.from(fontPath, 'binary')
const type = fileType(fontdata)
if (type && type.mime === 'font/ttf') {
data = fontdata.buffer
} else {

View file

@ -18,7 +18,7 @@ function nth (d) {
export function prettyDate (d) {
// 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()
}
@ -28,7 +28,7 @@ function chapterBars (chapters, currentChapter = -1, highlightCurrent = false) {
if (chapters.length <= 1) return null
let windowSize = 50
let wordCounts = []
let highestWordCount = chapters.reduce((max, ch) => {
const highestWordCount = chapters.reduce((max, ch) => {
wordCounts.push(ch.realWordCount)
if (ch.realWordCount > max) return ch.realWordCount
return max
@ -42,9 +42,9 @@ function chapterBars (chapters, currentChapter = -1, highlightCurrent = false) {
currentChapter -= start
}
wordCounts = wordCounts.map((c) => c / highestWordCount)
let barWidth = 9
let barSpacing = 2
let rowSpacing = 9
const barWidth = 9
const barSpacing = 2
const rowSpacing = 9
const barCount = Math.min(wordCounts.length, windowSize)
const rows = Math.floor(wordCounts.length / barCount) + 1
const rowHeight = 30 + rowSpacing
@ -68,9 +68,9 @@ function chapterBars (chapters, currentChapter = -1, highlightCurrent = false) {
}
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 || ''),
notes ? m('div#author_notes', { className: notesFirst ? 'top' : 'bottom' }, [
m('p', m('b', 'Author\'s Note:')),
@ -124,7 +124,7 @@ export function createChapter (ffc, ch, isNotesChapter) {
function sortSpineItems (items) {
let count = items.length
for (let i = 0; i < count; i++) {
let item = items[i]
const item = items[i]
if (!item) {
continue
}
@ -140,9 +140,9 @@ function sortSpineItems (items) {
}
export function createOpf (ffc) {
let remotes = []
const remotes = []
// let remoteCounter = 0
let remoteCache = new Set()
const remoteCache = new Set()
ffc.remoteResources.forEach((r, url) => {
// remoteCounter++
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 }))
})
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 })
)
let spineChapters = ffc.storyInfo.chapters.map((ch, num) =>
const spineChapters = ffc.storyInfo.chapters.map((ch, num) =>
m('itemref', { idref: 'chapter_' + zeroFill(3, num + 1) })
)
let manifestNotes = []
let spineNotes = []
const manifestNotes = []
const spineNotes = []
if (ffc.options.includeAuthorNotes && ffc.options.useAuthorNotesIndex && ffc.hasAuthorNotes) {
spineNotes.push(m('itemref', { idref: 'notesnav' }))
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' }))
spineNotes.push(m('itemref', { idref: id }))
})
@ -219,8 +219,8 @@ export function createOpf (ffc) {
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,
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' }),
@ -255,7 +255,7 @@ export function createOpf (ffc) {
function navPoints (list) {
let playOrder = 1
let arr = []
const arr = []
for (let i = 0; i < list.length; i++) {
if (!list[i]) continue
arr.push(m('navPoint', { id: 'navPoint-' + (i + 1), playOrder: playOrder++ }, [
@ -290,7 +290,7 @@ export function createNcx (ffc) {
}
export function createNav (ffc) {
let list = [
const 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
@ -299,7 +299,7 @@ export function createNav (ffc) {
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('.floatbox', m('span.wordcount', ch.realWordCount.toLocaleString('en-GB'))),
m('a', { href: 'Text/chapter_' + zeroFill(3, num + 1) + '.xhtml' }, ch.title),
@ -334,8 +334,8 @@ export function createNav (ffc) {
}
export function createNotesNav (ffc) {
let list = ffc.chaptersWithNotes.map((num) => {
let ch = ffc.storyInfo.chapters[num]
const list = ffc.chaptersWithNotes.map((num) => {
const ch = ffc.storyInfo.chapters[num]
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) {
let body
let { width, height } = ffc.coverImageDimensions
const { 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 },
@ -402,7 +402,7 @@ function infoBox (heading, data, title) {
function calcReadingTime (ffc, wordCount = 0) {
const wpm = ffc.options.wordsPerMinute
let time = (wordCount || ffc.totalWordCount) / wpm
const time = (wordCount || ffc.totalWordCount) / wpm
let value = 0
let unit = ''
if (time < 1) {

View file

@ -9,13 +9,13 @@ import { unicode } from './constants'
export function replaceAsync (str, re, callback) {
// http://es5.github.io/#x15.5.4.11
str = String(str)
let parts = []
const parts = []
let i = 0
if (Object.prototype.toString.call(re) === '[object RegExp]') {
if (re.global) { re.lastIndex = i }
let m
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))
i = re.lastIndex
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()
}
let frame = WebPRiffParser(data, 0).frames[0]
let width = [0]
let height = [0]
let decodedData = webpdecoder.WebPDecodeRGBA(
const frame = WebPRiffParser(data, 0).frames[0]
const width = [0]
const height = [0]
const decodedData = webpdecoder.WebPDecodeRGBA(
data,
frame['src_off'], frame['src_size'],
frame.src_off, frame.src_size,
width, height
)
let png = new PNGPacker({})
let buffers = []
const png = new PNGPacker({})
const buffers = []
png.on('data', (chunk) => {
buffers.push(chunk)
})
png.once('end', () => {
let pngData = Buffer.concat(buffers)
const pngData = Buffer.concat(buffers)
resolve(pngData)
})
png.pack(decodedData, width[0], height[0])
@ -122,7 +122,7 @@ export async function readingEase (text, wakeupInterval = Infinity, progresscb)
let lastTime = Date.now()
for (let i = 0; i < tokenSentences.length; i++) {
let now = Date.now()
const now = Date.now()
if (lastTime + wakeupInterval < now) {
lastTime = now
if (typeof progresscb === 'function') {

View file

@ -2,7 +2,7 @@
import path from 'path'
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 = {
entry: {