import JSZip from 'jszip' import escapeStringRegexp from 'escape-string-regexp' import { saveAs } from 'file-saver' import zeroFill from 'zero-fill' import { XmlEntities } from 'html-entities' import isNode from 'detect-node' import { styleCss, coverstyleCss, titlestyleCss } from './styles' import { cleanMarkup } from './cleanMarkup' import fetchRemote from './fetchRemote' import * as template from './templates' import { mimeMap, containerXml } from './constants' const entities = new XmlEntities() function blobToDataURL (blob, callback) { let a = new FileReader() a.onloadend = function (e) { callback(a.result) } a.readAsDataURL(blob) } export default class FimFic2Epub { constructor (storyId) { this.storyId = storyId this.isDownloading = false this.zip = null this.chapterContent = [] this.remoteResources = new Map() this.storyInfo = null this.isDownloading = false this.cachedBlob = null this.hasCoverImage = false this.includeTitlePage = true this.categories = [] this.tags = [] } download () { if (this.isDownloading) { alert("Calm down, I'm working on it (it's processing)") return } if (this.cachedBlob) { this.saveStory() return } this.build() } build () { this.isDownloading = true this.zip = new JSZip() this.zip.file('mimetype', 'application/epub+zip') this.zip.file('META-INF/container.xml', containerXml) console.log('Fetching story metadata...') fetchRemote('https://www.fimfiction.net/api/story.php?story=' + this.storyId, (raw, type) => { let data try { data = JSON.parse(raw) } catch (e) { console.log('Unable to fetch story json') return } if (data.error) { console.error(data.error) return } this.storyInfo = data.story this.storyInfo.chapters = this.storyInfo.chapters || [] this.storyInfo.uuid = 'urn:fimfiction:' + this.storyInfo.id this.zip.file('Styles/style.css', styleCss) this.zip.file('Styles/coverstyle.css', coverstyleCss) if (this.includeTitlePage) { this.zip.file('Styles/titlestyle.css', titlestyleCss) } this.zip.file('toc.ncx', template.createNcx(this)) this.zip.file('Text/nav.xhtml', template.createNav(this)) this.fetchTitlePage() }) } fetchTitlePage () { fetchRemote(this.storyInfo.url, (raw, type) => { this.extractTitlePageInfo(raw, () => this.checkCoverImage()) }) } extractTitlePageInfo (html, cb) { let descPos = html.indexOf('