mirror of
https://github.com/daniel-j/fimfic2epub.git
synced 2024-09-28 07:11:16 +12:00
v1.0.3
This commit is contained in:
parent
f4c0b59431
commit
b43e3156f5
8 changed files with 363 additions and 363 deletions
|
@ -4,7 +4,7 @@
|
||||||
"name": "fimfic2epub",
|
"name": "fimfic2epub",
|
||||||
"short_name": "ff2epub",
|
"short_name": "ff2epub",
|
||||||
"description": "Improved EPUB exporter for Fimfiction",
|
"description": "Improved EPUB exporter for Fimfiction",
|
||||||
"version": "1.0.2",
|
"version": "1.0.3",
|
||||||
|
|
||||||
"icons": {
|
"icons": {
|
||||||
"128": "icon-128.png"
|
"128": "icon-128.png"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "fimfic2epub.js",
|
"name": "fimfic2epub",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.0.1",
|
"version": "1.0.3",
|
||||||
"description": "",
|
"description": "",
|
||||||
"author": "djazz",
|
"author": "djazz",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
36
src/constants.js
Normal file
36
src/constants.js
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
|
||||||
|
export const NS = {
|
||||||
|
OPF: 'http://www.idpf.org/2007/opf',
|
||||||
|
OPS: 'http://www.idpf.org/2007/ops',
|
||||||
|
DC: 'http://purl.org/dc/elements/1.1/',
|
||||||
|
DAISY: 'http://www.daisy.org/z3986/2005/ncx/',
|
||||||
|
XHTML: 'http://www.w3.org/1999/xhtml',
|
||||||
|
SVG: 'http://www.w3.org/2000/svg',
|
||||||
|
XLINK: 'http://www.w3.org/1999/xlink'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const tidyOptions = {
|
||||||
|
'indent': 'auto',
|
||||||
|
'numeric-entities': 'yes',
|
||||||
|
'output-xhtml': 'yes',
|
||||||
|
'alt-text': 'Image',
|
||||||
|
'wrap': '0',
|
||||||
|
'quiet': 'yes',
|
||||||
|
'show-warnings': 0,
|
||||||
|
'newline': 'LF',
|
||||||
|
'tidy-mark': 'no'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mimeMap = {
|
||||||
|
'image/jpeg': 'Images/*.jpg',
|
||||||
|
'image/png': 'Images/*.png',
|
||||||
|
'image/gif': 'Images/*.gif'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const containerXml = `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
|
||||||
|
<rootfiles>
|
||||||
|
<rootfile full-path="content.opf" media-type="application/oebps-package+xml"/>
|
||||||
|
</rootfiles>
|
||||||
|
</container>
|
||||||
|
`
|
|
@ -1,28 +1,11 @@
|
||||||
/* global chrome, safari */
|
/* global chrome, safari */
|
||||||
|
|
||||||
function fetch (url, cb, type) {
|
import fetch from './fetch'
|
||||||
if (url.indexOf('//') === 0) {
|
|
||||||
url = 'http:' + url
|
|
||||||
}
|
|
||||||
let x = new XMLHttpRequest()
|
|
||||||
x.open('get', url, true)
|
|
||||||
if (type) {
|
|
||||||
x.responseType = type
|
|
||||||
}
|
|
||||||
x.onload = function () {
|
|
||||||
cb(x.response, x.getResponseHeader('content-type'))
|
|
||||||
}
|
|
||||||
x.onerror = function () {
|
|
||||||
console.error('error')
|
|
||||||
cb(null)
|
|
||||||
}
|
|
||||||
x.send()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof safari !== 'undefined') {
|
if (typeof safari !== 'undefined') {
|
||||||
safari.application.addEventListener('message', function (ev) {
|
safari.application.addEventListener('message', function (ev) {
|
||||||
let url = ev.message
|
let url = ev.message
|
||||||
fetch(url, function (buffer, type) {
|
fetch(url, (buffer, type) => {
|
||||||
console.log('Fetched ' + url + ' (' + type + ')')
|
console.log('Fetched ' + url + ' (' + type + ')')
|
||||||
ev.target.page.dispatchMessage('remote', {
|
ev.target.page.dispatchMessage('remote', {
|
||||||
input: url,
|
input: url,
|
||||||
|
@ -35,7 +18,7 @@ if (typeof safari !== 'undefined') {
|
||||||
let onMessage = chrome.extension.onMessage ? chrome.extension.onMessage : chrome.runtime.onMessage
|
let onMessage = chrome.extension.onMessage ? chrome.extension.onMessage : chrome.runtime.onMessage
|
||||||
|
|
||||||
onMessage.addListener(function (request, sender, sendResponse) {
|
onMessage.addListener(function (request, sender, sendResponse) {
|
||||||
fetch(request, function (blob, type) {
|
fetch(request, (blob, type) => {
|
||||||
sendResponse(URL.createObjectURL(blob), type)
|
sendResponse(URL.createObjectURL(blob), type)
|
||||||
}, 'blob')
|
}, 'blob')
|
||||||
return true
|
return true
|
||||||
|
|
19
src/fetch.js
Normal file
19
src/fetch.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
export default function fetch (url, cb, type) {
|
||||||
|
if (url.indexOf('//') === 0) {
|
||||||
|
url = 'http:' + url
|
||||||
|
}
|
||||||
|
let x = new XMLHttpRequest()
|
||||||
|
x.open('get', url, true)
|
||||||
|
if (type) {
|
||||||
|
x.responseType = type
|
||||||
|
}
|
||||||
|
x.onload = function () {
|
||||||
|
cb(x.response, x.getResponseHeader('content-type'))
|
||||||
|
}
|
||||||
|
x.onerror = function () {
|
||||||
|
console.error('error')
|
||||||
|
cb(null)
|
||||||
|
}
|
||||||
|
x.send()
|
||||||
|
}
|
413
src/main.js
413
src/main.js
|
@ -1,53 +1,24 @@
|
||||||
/* global chrome, safari */
|
/* global chrome, safari */
|
||||||
|
|
||||||
import JSZip from 'jszip'
|
import JSZip from 'jszip'
|
||||||
import m from 'mithril'
|
|
||||||
import render from './lib/mithril-node-render'
|
|
||||||
import { pd as pretty } from 'pretty-data'
|
|
||||||
import escapeStringRegexp from 'escape-string-regexp'
|
import escapeStringRegexp from 'escape-string-regexp'
|
||||||
import { XmlEntities } from 'html-entities'
|
|
||||||
import { saveAs } from 'file-saver'
|
import { saveAs } from 'file-saver'
|
||||||
import tidy from 'exports?tidy_html5!tidy-html5'
|
|
||||||
import zeroFill from 'zero-fill'
|
import zeroFill from 'zero-fill'
|
||||||
|
|
||||||
import styleCss from './style'
|
import styleCss from './style'
|
||||||
import coverstyleCss from './coverstyle'
|
import coverstyleCss from './coverstyle'
|
||||||
|
|
||||||
const entities = new XmlEntities()
|
import fetch from './fetch'
|
||||||
|
import parseChapter from './parseChapter'
|
||||||
const NS = {
|
import * as template from './templates'
|
||||||
OPF: 'http://www.idpf.org/2007/opf',
|
import { mimeMap, containerXml } from './constants'
|
||||||
OPS: 'http://www.idpf.org/2007/ops',
|
|
||||||
DC: 'http://purl.org/dc/elements/1.1/',
|
|
||||||
DAISY: 'http://www.daisy.org/z3986/2005/ncx/',
|
|
||||||
XHTML: 'http://www.w3.org/1999/xhtml',
|
|
||||||
SVG: 'http://www.w3.org/2000/svg',
|
|
||||||
XLINK: 'http://www.w3.org/1999/xlink'
|
|
||||||
}
|
|
||||||
|
|
||||||
let tidyOptions = {
|
|
||||||
'indent': 'auto',
|
|
||||||
'numeric-entities': 'yes',
|
|
||||||
'output-xhtml': 'yes',
|
|
||||||
'alt-text': 'Image',
|
|
||||||
'wrap': '0',
|
|
||||||
'quiet': 'yes',
|
|
||||||
'show-warnings': 0,
|
|
||||||
'newline': 'LF',
|
|
||||||
'tidy-mark': 'no'
|
|
||||||
}
|
|
||||||
|
|
||||||
let mimeMap = {
|
|
||||||
'image/jpeg': 'Images/*.jpg',
|
|
||||||
'image/png': 'Images/*.png',
|
|
||||||
'image/gif': 'Images/*.gif'
|
|
||||||
}
|
|
||||||
|
|
||||||
const STORY_ID = document.location.pathname.match(/^\/story\/(\d*)/)[1]
|
const STORY_ID = document.location.pathname.match(/^\/story\/(\d*)/)[1]
|
||||||
|
|
||||||
let storyInfo
|
let storyInfo
|
||||||
let remoteResources = new Map()
|
let remoteResources = new Map()
|
||||||
let chapterContent = []
|
let chapterContent = []
|
||||||
|
let safariQueue = {}
|
||||||
|
|
||||||
let epubButton = document.querySelector('.story_container ul.chapters li.bottom a[title="Download Story (.epub)"]')
|
let epubButton = document.querySelector('.story_container ul.chapters li.bottom a[title="Download Story (.epub)"]')
|
||||||
let isDownloading = false
|
let isDownloading = false
|
||||||
|
@ -68,47 +39,25 @@ if (epubButton) {
|
||||||
}, false)
|
}, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetch (url, cb, type) {
|
function blobToDataURL (blob, callback) {
|
||||||
if (url.indexOf('//') === 0) {
|
let a = new FileReader()
|
||||||
url = 'http:' + url
|
a.onloadend = function (e) { callback(a.result) }
|
||||||
}
|
a.readAsDataURL(blob)
|
||||||
let x = new XMLHttpRequest()
|
|
||||||
x.open('get', url, true)
|
|
||||||
if (type) {
|
|
||||||
x.responseType = type
|
|
||||||
}
|
|
||||||
x.onload = function () {
|
|
||||||
cb(x.response, x.getResponseHeader('content-type'))
|
|
||||||
}
|
|
||||||
x.onerror = function () {
|
|
||||||
cb(null)
|
|
||||||
}
|
|
||||||
x.send()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchChapters (cb) {
|
function saveStory () {
|
||||||
let chapters = storyInfo.chapters
|
console.log('Saving epub...')
|
||||||
let chapterCount = storyInfo.chapters.length
|
if (typeof safari !== 'undefined') {
|
||||||
let currentChapter = 0
|
blobToDataURL(cachedBlob, (dataurl) => {
|
||||||
function recursive () {
|
document.location.href = dataurl
|
||||||
let ch = chapters[currentChapter]
|
alert('Rename downloaded file to .epub')
|
||||||
console.log('Fetching chapter ' + (currentChapter + 1) + ' of ' + chapters.length + ': ' + ch.title)
|
|
||||||
fetchRemote(ch.link.replace('http', 'https'), function (html) {
|
|
||||||
html = parseChapter(currentChapter, ch, html)
|
|
||||||
chapterContent[currentChapter] = html
|
|
||||||
currentChapter++
|
|
||||||
if (currentChapter < chapterCount) {
|
|
||||||
recursive()
|
|
||||||
} else {
|
|
||||||
cb()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
saveAs(cachedBlob, storyInfo.title + ' by ' + storyInfo.author.name + '.epub')
|
||||||
}
|
}
|
||||||
recursive()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let safariQueue = {}
|
// messaging with the safari extension global page
|
||||||
|
|
||||||
function safariHandler (ev) {
|
function safariHandler (ev) {
|
||||||
let type = ev.message.type
|
let type = ev.message.type
|
||||||
let url = ev.message.input
|
let url = ev.message.input
|
||||||
|
@ -133,14 +82,6 @@ function safariHandler (ev) {
|
||||||
cb(fr.result, type)
|
cb(fr.result, type)
|
||||||
}
|
}
|
||||||
fr.readAsText(blob)
|
fr.readAsText(blob)
|
||||||
/*
|
|
||||||
let str = ''
|
|
||||||
let arr = new Uint8Array(data)
|
|
||||||
for (let i = 0; i < arr.length; i++) {
|
|
||||||
str += String.fromCharCode(arr[i])
|
|
||||||
}
|
|
||||||
cb(str, type)
|
|
||||||
*/
|
|
||||||
} else {
|
} else {
|
||||||
cb(data, type)
|
cb(data, type)
|
||||||
}
|
}
|
||||||
|
@ -150,7 +91,7 @@ if (typeof safari !== 'undefined') {
|
||||||
safari.self.addEventListener('message', safariHandler, false)
|
safari.self.addEventListener('message', safariHandler, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchRemote (url, cb, responseType) {
|
function fetchBackground (url, cb, responseType) {
|
||||||
if (typeof chrome !== 'undefined' && chrome.runtime.sendMessage) {
|
if (typeof chrome !== 'undefined' && chrome.runtime.sendMessage) {
|
||||||
chrome.runtime.sendMessage(url, function (objurl) {
|
chrome.runtime.sendMessage(url, function (objurl) {
|
||||||
fetch(objurl, cb, responseType)
|
fetch(objurl, cb, responseType)
|
||||||
|
@ -162,6 +103,23 @@ function fetchRemote (url, cb, responseType) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fetchRemote (url, cb, responseType) {
|
||||||
|
if (url.indexOf('//') === 0) {
|
||||||
|
url = 'http:' + url
|
||||||
|
}
|
||||||
|
if (document.location.protocol === 'https:' && url.indexOf('http:') === 0) {
|
||||||
|
fetchBackground(url, cb, responseType)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fetch(url, (data, type) => {
|
||||||
|
if (!data) {
|
||||||
|
fetchBackground(url, cb, responseType)
|
||||||
|
} else {
|
||||||
|
cb(data, type)
|
||||||
|
}
|
||||||
|
}, responseType)
|
||||||
|
}
|
||||||
|
|
||||||
function fetchRemoteFiles (zip, cb) {
|
function fetchRemoteFiles (zip, cb) {
|
||||||
let iter = remoteResources.entries()
|
let iter = remoteResources.entries()
|
||||||
let counter = 0
|
let counter = 0
|
||||||
|
@ -175,7 +133,7 @@ function fetchRemoteFiles (zip, cb) {
|
||||||
let url = r[0]
|
let url = r[0]
|
||||||
r = r[1]
|
r = r[1]
|
||||||
console.log('Fetching remote file ' + (counter + 1) + ' of ' + remoteResources.size + ': ' + r.filename, url)
|
console.log('Fetching remote file ' + (counter + 1) + ' of ' + remoteResources.size + ': ' + r.filename, url)
|
||||||
fetchRemote(url, function (data, type) {
|
fetchRemote(url, (data, type) => {
|
||||||
r.dest = null
|
r.dest = null
|
||||||
r.type = type
|
r.type = type
|
||||||
let dest = mimeMap[type]
|
let dest = mimeMap[type]
|
||||||
|
@ -191,22 +149,38 @@ function fetchRemoteFiles (zip, cb) {
|
||||||
recursive()
|
recursive()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fetchChapters (cb) {
|
||||||
|
let chapters = storyInfo.chapters
|
||||||
|
let chapterCount = storyInfo.chapters.length
|
||||||
|
let currentChapter = 0
|
||||||
|
function recursive () {
|
||||||
|
let ch = chapters[currentChapter]
|
||||||
|
console.log('Fetching chapter ' + (currentChapter + 1) + ' of ' + chapters.length + ': ' + ch.title)
|
||||||
|
fetchRemote(ch.link.replace('http', 'https'), (html) => {
|
||||||
|
parseChapter(currentChapter, ch, html, remoteResources, (html) => {
|
||||||
|
chapterContent[currentChapter] = html
|
||||||
|
currentChapter++
|
||||||
|
if (currentChapter < chapterCount) {
|
||||||
|
recursive()
|
||||||
|
} else {
|
||||||
|
cb()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
recursive()
|
||||||
|
}
|
||||||
|
|
||||||
function downloadStory () {
|
function downloadStory () {
|
||||||
isDownloading = true
|
isDownloading = true
|
||||||
|
|
||||||
const zip = new JSZip()
|
const zip = new JSZip()
|
||||||
zip.file('mimetype', 'application/epub+zip')
|
zip.file('mimetype', 'application/epub+zip')
|
||||||
zip.folder('META-INF').file('container.xml', `<?xml version="1.0" encoding="UTF-8"?>
|
zip.folder('META-INF').file('container.xml', containerXml)
|
||||||
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
|
|
||||||
<rootfiles>
|
|
||||||
<rootfile full-path="content.opf" media-type="application/oebps-package+xml"/>
|
|
||||||
</rootfiles>
|
|
||||||
</container>
|
|
||||||
`)
|
|
||||||
|
|
||||||
console.log('Fetching story metadata...')
|
console.log('Fetching story metadata...')
|
||||||
|
|
||||||
fetchRemote('https://www.fimfiction.net/api/story.php?story=' + STORY_ID, function (raw, type) {
|
fetchRemote('https://www.fimfiction.net/api/story.php?story=' + STORY_ID, (raw, type) => {
|
||||||
let data
|
let data
|
||||||
try {
|
try {
|
||||||
data = JSON.parse(raw)
|
data = JSON.parse(raw)
|
||||||
|
@ -225,20 +199,22 @@ function downloadStory () {
|
||||||
zip.file('style.css', styleCss)
|
zip.file('style.css', styleCss)
|
||||||
zip.file('coverstyle.css', coverstyleCss)
|
zip.file('coverstyle.css', coverstyleCss)
|
||||||
|
|
||||||
coverImage.addEventListener('load', function () {
|
coverImage.addEventListener('load', () => {
|
||||||
zip.file('toc.ncx', createNcx())
|
zip.file('toc.ncx', template.createNcx(storyInfo))
|
||||||
zip.file('nav.xhtml', createNav())
|
zip.file('nav.xhtml', template.createNav(storyInfo))
|
||||||
|
|
||||||
fetchChapters(function () {
|
fetchChapters(() => {
|
||||||
fetchRemoteFiles(zip, function () {
|
fetchRemoteFiles(zip, () => {
|
||||||
|
let coverFilename = ''
|
||||||
remoteResources.forEach((r, url) => {
|
remoteResources.forEach((r, url) => {
|
||||||
if (typeof r.chapter !== 'undefined' && r.originalUrl && r.dest) {
|
if (typeof r.chapter !== 'undefined' && r.originalUrl && r.dest) {
|
||||||
chapterContent[r.chapter] = chapterContent[r.chapter].replace(
|
chapterContent[r.chapter] = chapterContent[r.chapter].replace(
|
||||||
new RegExp(escapeStringRegexp(r.originalUrl), 'g'),
|
new RegExp(escapeStringRegexp(r.originalUrl), 'g'),
|
||||||
r.dest
|
r.dest
|
||||||
)
|
)
|
||||||
} else {
|
}
|
||||||
r.remote = true
|
if (r.filename === 'cover') {
|
||||||
|
coverFilename = r.dest
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -248,25 +224,10 @@ function downloadStory () {
|
||||||
zip.file(filename, html)
|
zip.file(filename, html)
|
||||||
}
|
}
|
||||||
|
|
||||||
zip.file('cover.xhtml', createCoverPage(coverImage.width, coverImage.height))
|
chapterContent.length = 0
|
||||||
zip.file('content.opf', createOpf())
|
|
||||||
|
|
||||||
/*
|
zip.file('cover.xhtml', template.createCoverPage(coverFilename, coverImage.width, coverImage.height))
|
||||||
zip
|
zip.file('content.opf', template.createOpf(storyInfo, remoteResources))
|
||||||
.generateNodeStream({
|
|
||||||
type: 'nodebuffer',
|
|
||||||
streamFiles: true,
|
|
||||||
mimeType: 'application/epub+zip',
|
|
||||||
compression: 'DEFLATE',
|
|
||||||
compressionOptions: {level: 9}
|
|
||||||
})
|
|
||||||
.pipe(fs.createWriteStream('out.epub'))
|
|
||||||
.on('finish', function () {
|
|
||||||
// JSZip generates a readable stream with a "end" event,
|
|
||||||
// but is piped here in a writable stream which emits a "finish" event.
|
|
||||||
console.log("out.epub written.");
|
|
||||||
})
|
|
||||||
*/
|
|
||||||
|
|
||||||
console.log('Packaging epub...')
|
console.log('Packaging epub...')
|
||||||
|
|
||||||
|
@ -287,231 +248,3 @@ function downloadStory () {
|
||||||
}, false)
|
}, false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function blobToDataURL (blob, callback) {
|
|
||||||
let a = new FileReader()
|
|
||||||
a.onloadend = function (e) { callback(a.result) }
|
|
||||||
a.readAsDataURL(blob)
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveStory () {
|
|
||||||
console.log('Saving epub...')
|
|
||||||
if (typeof safari !== 'undefined') {
|
|
||||||
blobToDataURL(cachedBlob, function (dataurl) {
|
|
||||||
document.location.href = dataurl
|
|
||||||
alert('Rename downloaded file to .epub')
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
saveAs(cachedBlob, storyInfo.title + ' by ' + storyInfo.author.name + '.epub')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseChapter (num, ch, html) {
|
|
||||||
let chapterTitle = html.match(/<a\s+[^>]*id="chapter_title"[^>]*>(.*?)<\/a>/)
|
|
||||||
|
|
||||||
if (!chapterTitle) {
|
|
||||||
return tidy('<?xml version="1.0" encoding="utf-8"?>\n' + chapterPage, tidyOptions)
|
|
||||||
}
|
|
||||||
chapterTitle = chapterTitle[1]
|
|
||||||
|
|
||||||
let chapterPos = html.indexOf('<div id="chapter_container">')
|
|
||||||
let chapter = html.substring(chapterPos + 29)
|
|
||||||
|
|
||||||
let pos = chapter.indexOf('\t</div>\t\t\n\t')
|
|
||||||
|
|
||||||
let authorNotesPos = chapter.substring(pos).indexOf('<b>Author\'s Note:</b>')
|
|
||||||
let authorNotes = ''
|
|
||||||
if (authorNotesPos !== -1) {
|
|
||||||
authorNotes = chapter.substring(pos + authorNotesPos + 22)
|
|
||||||
authorNotes = authorNotes.substring(0, authorNotes.indexOf('\t\t\n\t</div>'))
|
|
||||||
}
|
|
||||||
|
|
||||||
chapter = chapter.substring(0, pos)
|
|
||||||
|
|
||||||
let chapterPage = '<!doctype html>' + render(
|
|
||||||
m('html', {xmlns: NS.XHTML}, [
|
|
||||||
m('head', [
|
|
||||||
m('meta', {charset: 'utf-8'}),
|
|
||||||
m('link', {rel: 'stylesheet', type: 'text/css', href: 'style.css'}),
|
|
||||||
m('title', ch.title)
|
|
||||||
]),
|
|
||||||
m('body', [
|
|
||||||
m('div#chapter_container', m.trust(chapter)),
|
|
||||||
authorNotes ? m('div#author_notes', m.trust(authorNotes)) : null
|
|
||||||
])
|
|
||||||
])
|
|
||||||
)
|
|
||||||
|
|
||||||
chapterPage = chapterPage.replace(/<center>/g, '<div style="text-align: center;">')
|
|
||||||
chapterPage = chapterPage.replace(/<\/center>/g, '</div>')
|
|
||||||
|
|
||||||
chapterPage = chapterPage.replace(/<div class="youtube_container">(.+?)<\/div>/g, function (match, contents, offset) {
|
|
||||||
// console.log(match, contents, offset)
|
|
||||||
let youtubeId = contents.match(/src="https:\/\/www.youtube.com\/embed\/(.+?)"/)[1]
|
|
||||||
let thumbnail = 'http://img.youtube.com/vi/' + youtubeId + '/hqdefault.jpg'
|
|
||||||
let youtubeUrl = 'https://youtube.com/watch?v=' + youtubeId
|
|
||||||
return render(m('a', {href: youtubeUrl, target: '_blank'},
|
|
||||||
m('img', {src: thumbnail, alt: 'Youtube Video'})
|
|
||||||
))
|
|
||||||
})
|
|
||||||
|
|
||||||
chapterPage = chapterPage.replace('<blockquote style="margin: 10px 0px; box-sizing:border-box; -moz-box-sizing:border-box;margin-right:25px; padding: 15px;background-color: #F7F7F7;border: 1px solid #AAA;width: 50%;float:left;box-shadow: 5px 5px 0px #EEE;">', '<blockquote class="left_insert">')
|
|
||||||
chapterPage = chapterPage.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">')
|
|
||||||
|
|
||||||
chapterPage = tidy(`<?xml version="1.0" encoding="utf-8"?>\n` + chapterPage, tidyOptions)
|
|
||||||
|
|
||||||
let remoteCounter = 1
|
|
||||||
chapterPage = chapterPage.replace(/(<img src=")(.+?)("[^>]*>)/g, function (match, first, url, last) {
|
|
||||||
let cleanurl = decodeURI(entities.decode(url))
|
|
||||||
if (remoteResources.has(cleanurl)) {
|
|
||||||
return match
|
|
||||||
}
|
|
||||||
let filename = 'ch_' + zeroFill(3, num + 1) + '_' + remoteCounter
|
|
||||||
remoteCounter++
|
|
||||||
remoteResources.set(cleanurl, {filename: filename, chapter: num, originalUrl: url})
|
|
||||||
return match
|
|
||||||
})
|
|
||||||
|
|
||||||
return chapterPage
|
|
||||||
}
|
|
||||||
|
|
||||||
function subjects (s) {
|
|
||||||
let list = []
|
|
||||||
for (let i = 0; i < s.length; i++) {
|
|
||||||
list.push(m('dc:subject', s[i]))
|
|
||||||
}
|
|
||||||
return list
|
|
||||||
}
|
|
||||||
|
|
||||||
function createOpf () {
|
|
||||||
let remotes = []
|
|
||||||
remoteResources.forEach((r, url) => {
|
|
||||||
if (!r.dest) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let attrs = {id: r.filename, href: r.dest, 'media-type': r.type}
|
|
||||||
if (r.filename === 'cover') {
|
|
||||||
attrs.properties = 'cover-image'
|
|
||||||
}
|
|
||||||
remotes.push(m('item', attrs))
|
|
||||||
})
|
|
||||||
|
|
||||||
let contentOpf = '<?xml version="1.0" encoding="utf-8"?>\n' + pretty.xml(render(
|
|
||||||
m('package', {xmlns: NS.OPF, version: '3.0', 'unique-identifier': 'BookId'}, [
|
|
||||||
m('metadata', {'xmlns:dc': NS.DC, 'xmlns:opf': NS.OPF}, [
|
|
||||||
m('dc:identifier#BookId', storyInfo.uuid),
|
|
||||||
m('dc:title', storyInfo.title),
|
|
||||||
m('dc:creator#cre', storyInfo.author.name),
|
|
||||||
m('meta', {refines: '#cre', property: 'role', scheme: 'marc:relators'}, 'aut'),
|
|
||||||
m('dc:date', storyInfo.publishDate),
|
|
||||||
m('dc:publisher', 'Fimfiction'),
|
|
||||||
m('dc:description', storyInfo.description),
|
|
||||||
m('dc:source', storyInfo.url),
|
|
||||||
m('dc:language', 'en'),
|
|
||||||
m('meta', {name: 'cover', content: 'cover'}),
|
|
||||||
m('meta', {property: 'dcterms:modified'}, new Date(storyInfo.date_modified * 1000).toISOString().replace('.000', ''))
|
|
||||||
].concat(subjects(['Fiction', 'Pony']))),
|
|
||||||
|
|
||||||
m('manifest', [
|
|
||||||
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'}),
|
|
||||||
m('item', {id: 'style', href: 'style.css', 'media-type': 'text/css'}),
|
|
||||||
m('item', {id: 'coverstyle', href: 'coverstyle.css', 'media-type': 'text/css'}),
|
|
||||||
m('item', {id: 'coverpage', href: 'cover.xhtml', 'media-type': 'application/xhtml+xml', properties: 'svg'})
|
|
||||||
].concat(storyInfo.chapters.map((ch, num) =>
|
|
||||||
m('item', {id: 'chapter_' + zeroFill(3, num + 1), href: 'chapter_' + zeroFill(3, num + 1) + '.xhtml', 'media-type': 'application/xhtml+xml'})
|
|
||||||
), remotes)),
|
|
||||||
|
|
||||||
m('spine', {toc: 'ncx'}, [
|
|
||||||
m('itemref', {idref: 'coverpage'}),
|
|
||||||
m('itemref', {idref: 'nav'})
|
|
||||||
].concat(storyInfo.chapters.map((ch, num) =>
|
|
||||||
m('itemref', {idref: 'chapter_' + zeroFill(3, num + 1)})
|
|
||||||
))),
|
|
||||||
|
|
||||||
false ? m('guide', [
|
|
||||||
|
|
||||||
]) : null
|
|
||||||
])
|
|
||||||
))
|
|
||||||
// console.log(contentOpf)
|
|
||||||
return contentOpf
|
|
||||||
}
|
|
||||||
|
|
||||||
function navPoints (list) {
|
|
||||||
let arr = []
|
|
||||||
for (let i = 0; i < list.length; i++) {
|
|
||||||
list[i]
|
|
||||||
arr.push(m('navPoint', {id: 'navPoint-' + (i + 1), playOrder: i + 1}, [
|
|
||||||
m('navLabel', m('text', list[i][0])),
|
|
||||||
m('content', {src: list[i][1]})
|
|
||||||
]))
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
|
|
||||||
function createNcx () {
|
|
||||||
let tocNcx = '<?xml version="1.0" encoding="utf-8" ?>\n' + pretty.xml(render(
|
|
||||||
m('ncx', {version: '2005-1', xmlns: NS.DAISY}, [
|
|
||||||
m('head', [
|
|
||||||
m('meta', {content: storyInfo.uuid, name: 'dtb:uid'}),
|
|
||||||
m('meta', {content: 0, name: 'dtb:depth'}),
|
|
||||||
m('meta', {content: 0, name: 'dtb:totalPageCount'}),
|
|
||||||
m('meta', {content: 0, name: 'dtb:maxPageNumber'})
|
|
||||||
]),
|
|
||||||
m('docTitle', m('text', storyInfo.title)),
|
|
||||||
m('navMap', navPoints([
|
|
||||||
['Cover', 'cover.xhtml'],
|
|
||||||
['Contents', 'nav.xhtml']
|
|
||||||
].concat(storyInfo.chapters.map((ch, num) =>
|
|
||||||
[ch.title, 'chapter_' + zeroFill(3, num + 1) + '.xhtml']
|
|
||||||
))))
|
|
||||||
])
|
|
||||||
))
|
|
||||||
// console.log(tocNcx)
|
|
||||||
return tocNcx
|
|
||||||
}
|
|
||||||
|
|
||||||
function createNav () {
|
|
||||||
let navDocument = '<?xml version="1.0" encoding="utf-8"?>\n<!DOCTYPE html>\n' + pretty.xml(render(
|
|
||||||
m('html', {xmlns: NS.XHTML, 'xmlns:epub': NS.OPS, lang: 'en', 'xml:lang': 'en'}, [
|
|
||||||
m('head', [
|
|
||||||
m('meta', {charset: 'utf-8'}),
|
|
||||||
m('link', {rel: 'stylesheet', type: 'text/css', href: 'style.css'}),
|
|
||||||
m('title', 'Contents')
|
|
||||||
]),
|
|
||||||
m('body', [
|
|
||||||
m('nav#toc', {'epub:type': 'toc'}, [
|
|
||||||
m('h1', 'Contents'),
|
|
||||||
m('ol', [
|
|
||||||
m('li', {hidden: ''}, m('a', {href: 'cover.xhtml'}, 'Cover')),
|
|
||||||
m('li', {hidden: ''}, m('a', {href: 'nav.xhtml'}, 'Contents'))
|
|
||||||
].concat(storyInfo.chapters.map((ch, num) =>
|
|
||||||
m('li', m('a', {href: 'chapter_' + zeroFill(3, num + 1) + '.xhtml'}, ch.title))
|
|
||||||
)))
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
))
|
|
||||||
// console.log(navDocument)
|
|
||||||
return navDocument
|
|
||||||
}
|
|
||||||
|
|
||||||
function createCoverPage (w, h) {
|
|
||||||
let coverPage = '<?xml version="1.0" encoding="utf-8"?>\n<!DOCTYPE html>\n' + pretty.xml(render(
|
|
||||||
m('html', {xmlns: NS.XHTML, 'xmlns:epub': NS.OPS}, [
|
|
||||||
m('head', [
|
|
||||||
m('meta', {name: 'viewport', content: 'width=' + w + ', height=' + h}),
|
|
||||||
m('title', 'Cover'),
|
|
||||||
m('link', {rel: 'stylesheet', type: 'text/css', href: 'coverstyle.css'})
|
|
||||||
]),
|
|
||||||
m('body', {'epub:type': 'cover'}, [
|
|
||||||
m('svg#cover', {xmlns: NS.SVG, 'xmlns:xlink': NS.XLINK, version: '1.1', viewBox: '0 0 ' + w + ' ' + h},
|
|
||||||
m('image', {width: w, height: h, 'xlink:href': 'Images/cover.jpg'})
|
|
||||||
)
|
|
||||||
])
|
|
||||||
])
|
|
||||||
))
|
|
||||||
// console.log(coverPage)
|
|
||||||
return coverPage
|
|
||||||
}
|
|
||||||
|
|
81
src/parseChapter.js
Normal file
81
src/parseChapter.js
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
|
||||||
|
import m from 'mithril'
|
||||||
|
import render from './lib/mithril-node-render'
|
||||||
|
import { XmlEntities } from 'html-entities'
|
||||||
|
import tidy from 'exports?tidy_html5!tidy-html5'
|
||||||
|
import zeroFill from 'zero-fill'
|
||||||
|
|
||||||
|
import { NS, tidyOptions } from './constants'
|
||||||
|
|
||||||
|
const entities = new XmlEntities()
|
||||||
|
|
||||||
|
export default function parseChapter (num, ch, html, remoteResources, callback) {
|
||||||
|
let chapterTitle = html.match(/<a\s+[^>]*id="chapter_title"[^>]*>(.*?)<\/a>/)
|
||||||
|
|
||||||
|
if (!chapterTitle) {
|
||||||
|
return tidy('<?xml version="1.0" encoding="utf-8"?>\n' + chapterPage, tidyOptions)
|
||||||
|
}
|
||||||
|
chapterTitle = chapterTitle[1]
|
||||||
|
|
||||||
|
let chapterPos = html.indexOf('<div id="chapter_container">')
|
||||||
|
let chapter = html.substring(chapterPos + 29)
|
||||||
|
|
||||||
|
let pos = chapter.indexOf('\t</div>\t\t\n\t')
|
||||||
|
|
||||||
|
let authorNotesPos = chapter.substring(pos).indexOf('<b>Author\'s Note:</b>')
|
||||||
|
let authorNotes = ''
|
||||||
|
if (authorNotesPos !== -1) {
|
||||||
|
authorNotes = chapter.substring(pos + authorNotesPos + 22)
|
||||||
|
authorNotes = authorNotes.substring(0, authorNotes.indexOf('\t\t\n\t</div>'))
|
||||||
|
}
|
||||||
|
|
||||||
|
chapter = chapter.substring(0, pos)
|
||||||
|
|
||||||
|
let chapterPage = '<!doctype html>' + render(
|
||||||
|
m('html', {xmlns: NS.XHTML}, [
|
||||||
|
m('head', [
|
||||||
|
m('meta', {charset: 'utf-8'}),
|
||||||
|
m('link', {rel: 'stylesheet', type: 'text/css', href: 'style.css'}),
|
||||||
|
m('title', ch.title)
|
||||||
|
]),
|
||||||
|
m('body', [
|
||||||
|
m('div#chapter_container', m.trust(chapter)),
|
||||||
|
authorNotes ? m('div#author_notes', m.trust(authorNotes)) : null
|
||||||
|
])
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
chapterPage = chapterPage.replace(/<center>/g, '<div style="text-align: center;">')
|
||||||
|
chapterPage = chapterPage.replace(/<\/center>/g, '</div>')
|
||||||
|
|
||||||
|
chapterPage = chapterPage.replace(/<div class="youtube_container">(.+?)<\/div>/g, (match, contents) => {
|
||||||
|
// console.log(match, contents)
|
||||||
|
let youtubeId = contents.match(/src="https:\/\/www.youtube.com\/embed\/(.+?)"/)[1]
|
||||||
|
let thumbnail = 'http://img.youtube.com/vi/' + youtubeId + '/hqdefault.jpg'
|
||||||
|
let youtubeUrl = 'https://youtube.com/watch?v=' + youtubeId
|
||||||
|
return render(m('a', {href: youtubeUrl, target: '_blank'},
|
||||||
|
m('img', {src: thumbnail, alt: 'Youtube Video'})
|
||||||
|
))
|
||||||
|
})
|
||||||
|
|
||||||
|
chapterPage = chapterPage.replace('<blockquote style="margin: 10px 0px; box-sizing:border-box; -moz-box-sizing:border-box;margin-right:25px; padding: 15px;background-color: #F7F7F7;border: 1px solid #AAA;width: 50%;float:left;box-shadow: 5px 5px 0px #EEE;">', '<blockquote class="left_insert">')
|
||||||
|
chapterPage = chapterPage.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">')
|
||||||
|
|
||||||
|
chapterPage = tidy(`<?xml version="1.0" encoding="utf-8"?>\n` + chapterPage, tidyOptions)
|
||||||
|
|
||||||
|
let remoteCounter = 1
|
||||||
|
let matchUrl = /<img src="(.+?)"[^>]*>/g
|
||||||
|
|
||||||
|
for (let ma; (ma = matchUrl.exec(chapterPage));) {
|
||||||
|
let url = ma[1]
|
||||||
|
let cleanurl = decodeURI(entities.decode(url))
|
||||||
|
if (remoteResources.has(cleanurl)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
let filename = 'ch_' + zeroFill(3, num + 1) + '_' + remoteCounter
|
||||||
|
remoteCounter++
|
||||||
|
remoteResources.set(cleanurl, {filename: filename, chapter: num, originalUrl: url})
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(chapterPage)
|
||||||
|
}
|
148
src/templates.js
Normal file
148
src/templates.js
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
|
||||||
|
import m from 'mithril'
|
||||||
|
import render from './lib/mithril-node-render'
|
||||||
|
import { pd as pretty } from 'pretty-data'
|
||||||
|
import zeroFill from 'zero-fill'
|
||||||
|
|
||||||
|
import { NS } from './constants'
|
||||||
|
|
||||||
|
function subjects (s) {
|
||||||
|
let list = []
|
||||||
|
for (let i = 0; i < s.length; i++) {
|
||||||
|
list.push(m('dc:subject', s[i]))
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createOpf (storyInfo, remoteResources) {
|
||||||
|
let remotes = []
|
||||||
|
remoteResources.forEach((r, url) => {
|
||||||
|
if (!r.dest) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let attrs = {id: r.filename, href: r.dest, 'media-type': r.type}
|
||||||
|
if (r.filename === 'cover') {
|
||||||
|
attrs.properties = 'cover-image'
|
||||||
|
}
|
||||||
|
remotes.push(m('item', attrs))
|
||||||
|
})
|
||||||
|
|
||||||
|
let contentOpf = '<?xml version="1.0" encoding="utf-8"?>\n' + pretty.xml(render(
|
||||||
|
m('package', {xmlns: NS.OPF, version: '3.0', 'unique-identifier': 'BookId'}, [
|
||||||
|
m('metadata', {'xmlns:dc': NS.DC, 'xmlns:opf': NS.OPF}, [
|
||||||
|
m('dc:identifier#BookId', storyInfo.uuid),
|
||||||
|
m('dc:title', storyInfo.title),
|
||||||
|
m('dc:creator#cre', storyInfo.author.name),
|
||||||
|
m('meta', {refines: '#cre', property: 'role', scheme: 'marc:relators'}, 'aut'),
|
||||||
|
m('dc:date', storyInfo.publishDate),
|
||||||
|
m('dc:publisher', 'Fimfiction'),
|
||||||
|
m('dc:description', storyInfo.description),
|
||||||
|
m('dc:source', storyInfo.url),
|
||||||
|
m('dc:language', 'en'),
|
||||||
|
m('meta', {name: 'cover', content: 'cover'}),
|
||||||
|
m('meta', {property: 'dcterms:modified'}, new Date(storyInfo.date_modified * 1000).toISOString().replace('.000', ''))
|
||||||
|
].concat(subjects(['Pony']))),
|
||||||
|
|
||||||
|
m('manifest', [
|
||||||
|
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'}),
|
||||||
|
m('item', {id: 'style', href: 'style.css', 'media-type': 'text/css'}),
|
||||||
|
m('item', {id: 'coverstyle', href: 'coverstyle.css', 'media-type': 'text/css'}),
|
||||||
|
m('item', {id: 'coverpage', href: 'cover.xhtml', 'media-type': 'application/xhtml+xml', properties: 'svg'})
|
||||||
|
].concat(storyInfo.chapters.map((ch, num) =>
|
||||||
|
m('item', {id: 'chapter_' + zeroFill(3, num + 1), href: 'chapter_' + zeroFill(3, num + 1) + '.xhtml', 'media-type': 'application/xhtml+xml'})
|
||||||
|
), remotes)),
|
||||||
|
|
||||||
|
m('spine', {toc: 'ncx'}, [
|
||||||
|
m('itemref', {idref: 'coverpage'}),
|
||||||
|
m('itemref', {idref: 'nav'})
|
||||||
|
].concat(storyInfo.chapters.map((ch, num) =>
|
||||||
|
m('itemref', {idref: 'chapter_' + zeroFill(3, num + 1)})
|
||||||
|
))),
|
||||||
|
|
||||||
|
false ? m('guide', [
|
||||||
|
|
||||||
|
]) : null
|
||||||
|
])
|
||||||
|
))
|
||||||
|
// console.log(contentOpf)
|
||||||
|
return contentOpf
|
||||||
|
}
|
||||||
|
|
||||||
|
function navPoints (list) {
|
||||||
|
let arr = []
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
list[i]
|
||||||
|
arr.push(m('navPoint', {id: 'navPoint-' + (i + 1), playOrder: i + 1}, [
|
||||||
|
m('navLabel', m('text', list[i][0])),
|
||||||
|
m('content', {src: list[i][1]})
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createNcx (storyInfo) {
|
||||||
|
let tocNcx = '<?xml version="1.0" encoding="utf-8" ?>\n' + pretty.xml(render(
|
||||||
|
m('ncx', {version: '2005-1', xmlns: NS.DAISY}, [
|
||||||
|
m('head', [
|
||||||
|
m('meta', {content: storyInfo.uuid, name: 'dtb:uid'}),
|
||||||
|
m('meta', {content: 0, name: 'dtb:depth'}),
|
||||||
|
m('meta', {content: 0, name: 'dtb:totalPageCount'}),
|
||||||
|
m('meta', {content: 0, name: 'dtb:maxPageNumber'})
|
||||||
|
]),
|
||||||
|
m('docTitle', m('text', storyInfo.title)),
|
||||||
|
m('navMap', navPoints([
|
||||||
|
['Cover', 'cover.xhtml'],
|
||||||
|
['Contents', 'nav.xhtml']
|
||||||
|
].concat(storyInfo.chapters.map((ch, num) =>
|
||||||
|
[ch.title, 'chapter_' + zeroFill(3, num + 1) + '.xhtml']
|
||||||
|
))))
|
||||||
|
])
|
||||||
|
))
|
||||||
|
// console.log(tocNcx)
|
||||||
|
return tocNcx
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createNav (storyInfo) {
|
||||||
|
let navDocument = '<?xml version="1.0" encoding="utf-8"?>\n<!DOCTYPE html>\n' + pretty.xml(render(
|
||||||
|
m('html', {xmlns: NS.XHTML, 'xmlns:epub': NS.OPS, lang: 'en', 'xml:lang': 'en'}, [
|
||||||
|
m('head', [
|
||||||
|
m('meta', {charset: 'utf-8'}),
|
||||||
|
m('link', {rel: 'stylesheet', type: 'text/css', href: 'style.css'}),
|
||||||
|
m('title', 'Contents')
|
||||||
|
]),
|
||||||
|
m('body', [
|
||||||
|
m('nav#toc', {'epub:type': 'toc'}, [
|
||||||
|
m('h1', 'Contents'),
|
||||||
|
m('ol', [
|
||||||
|
m('li', {hidden: ''}, m('a', {href: 'cover.xhtml'}, 'Cover')),
|
||||||
|
m('li', {hidden: ''}, m('a', {href: 'nav.xhtml'}, 'Contents'))
|
||||||
|
].concat(storyInfo.chapters.map((ch, num) =>
|
||||||
|
m('li', m('a', {href: 'chapter_' + zeroFill(3, num + 1) + '.xhtml'}, ch.title))
|
||||||
|
)))
|
||||||
|
])
|
||||||
|
])
|
||||||
|
])
|
||||||
|
))
|
||||||
|
// console.log(navDocument)
|
||||||
|
return navDocument
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCoverPage (coverFilename, w, h) {
|
||||||
|
let coverPage = '<?xml version="1.0" encoding="utf-8"?>\n<!DOCTYPE html>\n' + pretty.xml(render(
|
||||||
|
m('html', {xmlns: NS.XHTML, 'xmlns:epub': NS.OPS}, [
|
||||||
|
m('head', [
|
||||||
|
m('meta', {name: 'viewport', content: 'width=' + w + ', height=' + h}),
|
||||||
|
m('title', 'Cover'),
|
||||||
|
m('link', {rel: 'stylesheet', type: 'text/css', href: 'coverstyle.css'})
|
||||||
|
]),
|
||||||
|
m('body', {'epub:type': 'cover'}, [
|
||||||
|
m('svg#cover', {xmlns: NS.SVG, 'xmlns:xlink': NS.XLINK, version: '1.1', viewBox: '0 0 ' + w + ' ' + h},
|
||||||
|
m('image', {width: w, height: h, 'xlink:href': coverFilename})
|
||||||
|
)
|
||||||
|
])
|
||||||
|
])
|
||||||
|
))
|
||||||
|
// console.log(coverPage)
|
||||||
|
return coverPage
|
||||||
|
}
|
Loading…
Reference in a new issue