mirror of
https://github.com/daniel-j/fimfic2epub.git
synced 2024-06-23 08:30:20 +12:00
tidying up!
This commit is contained in:
parent
3e4d08f646
commit
4ae5731d14
|
@ -6,7 +6,7 @@
|
|||
z-index: 1001;
|
||||
}
|
||||
#epubDialogContainer .drop-down-pop-up-container .drop-down-pop-up {
|
||||
width: 500px;
|
||||
width: 600px;
|
||||
}
|
||||
#epubDialogContainer .drop-down-pop-up-container .drop-down-pop-up h1 {
|
||||
cursor: move;
|
||||
|
@ -16,10 +16,21 @@
|
|||
|
||||
}
|
||||
|
||||
#epubDialogContainer table.properties textarea {
|
||||
line-height: 1em;
|
||||
font-size: 1em;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
#epubDialogContainer table.properties td.label {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#epubDialogContainer table.properties input:invalid {
|
||||
background-repeat: no-repeat;
|
||||
background-position: 8px center;
|
||||
}
|
||||
|
||||
#epubDialogContainer div.rating_container {
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import { containerXml } from './constants'
|
|||
|
||||
const entities = new XmlEntities()
|
||||
|
||||
module.exports = class FimFic2Epub extends Emitter {
|
||||
class FimFic2Epub extends Emitter {
|
||||
|
||||
static getStoryId (id) {
|
||||
if (isNaN(id)) {
|
||||
|
@ -113,8 +113,10 @@ module.exports = class FimFic2Epub extends Emitter {
|
|||
|
||||
this.storyInfo = null
|
||||
this.description = ''
|
||||
this.subjects = []
|
||||
this.chapters = []
|
||||
this.remoteResources = new Map()
|
||||
this.coverUrl = ''
|
||||
this.coverImage = null
|
||||
this.coverFilename = ''
|
||||
this.coverType = ''
|
||||
|
@ -138,63 +140,54 @@ module.exports = class FimFic2Epub extends Emitter {
|
|||
this.coverImageDimensions = sizeOf(new Buffer(buffer))
|
||||
}
|
||||
|
||||
fetchMetadata () {
|
||||
this.storyInfo = null
|
||||
this.description = ''
|
||||
this.subjects.length = 0
|
||||
|
||||
return FimFic2Epub.fetchStoryInfo(this.storyId).then((storyInfo) => {
|
||||
this.storyInfo = storyInfo
|
||||
this.storyInfo.uuid = 'urn:fimfiction:' + this.storyInfo.id
|
||||
this.filename = FimFic2Epub.getFilename(this.storyInfo)
|
||||
this.progress(0, 0.3)
|
||||
})
|
||||
.then(this.fetchTitlePage.bind(this))
|
||||
.then(() => cleanMarkup(this.description)).then((html) => {
|
||||
this.storyInfo.description = html
|
||||
this.findRemoteResources('description', 'description', html)
|
||||
})
|
||||
}
|
||||
|
||||
fetch () {
|
||||
if (this.fetchPromise) {
|
||||
return this.fetchPromise
|
||||
}
|
||||
|
||||
this.storyInfo = null
|
||||
this.description = ''
|
||||
this.chapters.length = 0
|
||||
this.remoteResources.clear()
|
||||
|
||||
this.progress(0, 0, 'Fetching metadata...')
|
||||
this.progress(0, 0, 'Fetching...')
|
||||
|
||||
let p =
|
||||
FimFic2Epub.fetchStoryInfo(this.storyId).then((storyInfo) => {
|
||||
this.storyInfo = storyInfo
|
||||
this.storyInfo.uuid = 'urn:fimfiction:' + this.storyInfo.id
|
||||
this.filename = FimFic2Epub.getFilename(this.storyInfo)
|
||||
this.progress(0, 0.3)
|
||||
})
|
||||
.then(this.fetchTitlePage.bind(this))
|
||||
.then(() => cleanMarkup(this.description)).then((html) => {
|
||||
this.storyInfo.description = html
|
||||
this.findRemoteResources('description', 'description', html)
|
||||
})
|
||||
this.fetchPromise = Promise.resolve()
|
||||
if (!this.storyInfo) {
|
||||
this.fetchPromise = this.fetchPromise.then(this.fetchMetadata.bind(this))
|
||||
}
|
||||
this.fetchPromise = this.fetchPromise
|
||||
.then(this.fetchCoverImage.bind(this))
|
||||
.then(this.fetchChapters.bind(this))
|
||||
|
||||
// .then(this.processChapters.bind(this))
|
||||
.then(this.fetchRemoteFiles.bind(this))
|
||||
.then(() => {
|
||||
this.fetchPromise = null
|
||||
})
|
||||
|
||||
this.fetchPromise = p
|
||||
return p
|
||||
return this.fetchPromise
|
||||
}
|
||||
|
||||
build () {
|
||||
this.cachedFile = null
|
||||
this.zip = null
|
||||
|
||||
this.remoteResources.forEach((r, url) => {
|
||||
let dest = '../' + r.dest
|
||||
if (r.dest && r.originalUrl && r.where) {
|
||||
let ourl = new RegExp(escapeStringRegexp(r.originalUrl), 'g')
|
||||
for (var i = 0; i < r.where.length; i++) {
|
||||
let w = r.where[i]
|
||||
if (typeof w === 'number') {
|
||||
this.chapters[w] = this.chapters[w].replace(ourl, dest)
|
||||
} else if (w === 'description') {
|
||||
this.storyInfo.description = this.storyInfo.description.replace(ourl, dest)
|
||||
} else if (w === 'tags') {
|
||||
this.tags.byImage[r.originalUrl].image = dest
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
this.replaceRemoteResources()
|
||||
|
||||
this.zip = new JSZip()
|
||||
|
||||
|
@ -226,8 +219,6 @@ module.exports = class FimFic2Epub extends Emitter {
|
|||
this.remoteResources.forEach((r) => {
|
||||
this.zip.file('OEBPS/' + r.dest, r.data)
|
||||
})
|
||||
|
||||
this.progress(6, 0, 'Complete!')
|
||||
}
|
||||
|
||||
// for node, resolve a Buffer, in browser resolve a Blob
|
||||
|
@ -238,6 +229,7 @@ module.exports = class FimFic2Epub extends Emitter {
|
|||
if (this.cachedFile) {
|
||||
return Promise.resolve(this.cachedFile)
|
||||
}
|
||||
this.progress(6, 0, 'Compressing...')
|
||||
|
||||
return this.zip
|
||||
.generateAsync({
|
||||
|
@ -247,6 +239,7 @@ module.exports = class FimFic2Epub extends Emitter {
|
|||
compressionOptions: {level: 9}
|
||||
})
|
||||
.then((file) => {
|
||||
this.progress(6, 0.3, 'Complete!')
|
||||
this.cachedFile = file
|
||||
return file
|
||||
})
|
||||
|
@ -269,7 +262,7 @@ module.exports = class FimFic2Epub extends Emitter {
|
|||
|
||||
// Internal/private methods
|
||||
progress (part, percent, status) {
|
||||
let parts = 6
|
||||
let parts = 6.3
|
||||
let partsize = 1 / parts
|
||||
percent = (part / parts) + percent * partsize
|
||||
this.trigger('progress', percent, status)
|
||||
|
@ -305,7 +298,7 @@ module.exports = class FimFic2Epub extends Emitter {
|
|||
return this.coverImage
|
||||
}
|
||||
this.coverImage = null
|
||||
let url = this.storyInfo.full_image
|
||||
let url = this.coverUrl || this.storyInfo.full_image
|
||||
if (!url) {
|
||||
return null
|
||||
}
|
||||
|
@ -357,13 +350,16 @@ module.exports = class FimFic2Epub extends Emitter {
|
|||
html = html.substring(endCatsPos + 6)
|
||||
|
||||
let categories = []
|
||||
this.subjects.push('Fimfiction')
|
||||
let matchCategory = /<a href="(.*?)" class="(.*?)">(.*?)<\/a>/g
|
||||
for (let c; (c = matchCategory.exec(catsHtml));) {
|
||||
categories.push({
|
||||
let cat = {
|
||||
url: 'http://www.fimfiction.net' + c[1],
|
||||
className: c[2],
|
||||
name: entities.decode(c[3])
|
||||
})
|
||||
}
|
||||
categories.push(cat)
|
||||
this.subjects.push(cat.name)
|
||||
}
|
||||
this.categories = categories
|
||||
|
||||
|
@ -500,4 +496,25 @@ module.exports = class FimFic2Epub extends Emitter {
|
|||
recursive()
|
||||
})
|
||||
}
|
||||
|
||||
replaceRemoteResources () {
|
||||
this.remoteResources.forEach((r, url) => {
|
||||
let dest = '../' + r.dest
|
||||
if (r.dest && r.originalUrl && r.where) {
|
||||
let ourl = new RegExp(escapeStringRegexp(r.originalUrl), 'g')
|
||||
for (var i = 0; i < r.where.length; i++) {
|
||||
let w = r.where[i]
|
||||
if (typeof w === 'number') {
|
||||
this.chapters[w] = this.chapters[w].replace(ourl, dest)
|
||||
} else if (w === 'description') {
|
||||
this.storyInfo.description = this.storyInfo.description.replace(ourl, dest)
|
||||
} else if (w === 'tags') {
|
||||
this.tags.byImage[r.originalUrl].image = dest
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FimFic2Epub
|
||||
|
|
75
src/main.js
75
src/main.js
|
@ -5,10 +5,12 @@ import FimFic2Epub from './FimFic2Epub'
|
|||
import m from 'mithril'
|
||||
import { saveAs } from 'file-saver'
|
||||
|
||||
function blobToDataURL (blob, callback) {
|
||||
let fr = new FileReader()
|
||||
fr.onloadend = function (e) { callback(fr.result) }
|
||||
fr.readAsDataURL(blob)
|
||||
function blobToDataURL (blob) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let fr = new FileReader()
|
||||
fr.onloadend = function (e) { resolve(fr.result) }
|
||||
fr.readAsDataURL(blob)
|
||||
})
|
||||
}
|
||||
|
||||
function blobToArrayBuffer (blob) {
|
||||
|
@ -33,9 +35,9 @@ document.body.appendChild(dialogContainer)
|
|||
|
||||
let checkbox = {
|
||||
view: function (ctrl, args, text) {
|
||||
return m('label.toggleable-switch', [
|
||||
m('input', {type: 'checkbox', name: args.name, checked: args.checked}),
|
||||
m('a'),
|
||||
return m('label.toggleable-switch', {style: 'white-space: nowrap;'}, [
|
||||
m('input', {type: 'checkbox', name: args.name, checked: args.checked, onchange: args.onchange}),
|
||||
m('a', {style: 'margin-right: 10px'}),
|
||||
text
|
||||
])
|
||||
}
|
||||
|
@ -46,11 +48,15 @@ let ffcStatus = m.prop('')
|
|||
|
||||
let dialog = {
|
||||
controller (args) {
|
||||
this.isLoading = m.prop(true)
|
||||
this.dragging = m.prop(false)
|
||||
this.xpos = m.prop(0)
|
||||
this.ypos = m.prop(0)
|
||||
this.el = m.prop(null)
|
||||
this.coverFile = m.prop(null)
|
||||
this.coverUrl = m.prop('')
|
||||
this.checkboxCoverUrl = m.prop(false)
|
||||
this.subjects = m.prop(ffc.subjects)
|
||||
|
||||
this.setCoverFile = (e) => {
|
||||
this.coverFile(e.target.files ? e.target.files[0] : null)
|
||||
|
@ -76,26 +82,40 @@ let dialog = {
|
|||
window.addEventListener('mousemove', onmove, false)
|
||||
window.addEventListener('mouseup', onup, false)
|
||||
}
|
||||
this.onOpen = function (el, first) {
|
||||
if (!first) {
|
||||
this.onOpen = function (el, isInitialized) {
|
||||
if (!isInitialized) {
|
||||
this.el(el)
|
||||
let rect = this.el().firstChild.getBoundingClientRect()
|
||||
this.xpos((window.innerWidth / 2) - (rect.width / 2) + document.body.scrollLeft)
|
||||
this.ypos((window.innerHeight / 2) - (rect.height / 2) + document.body.scrollTop)
|
||||
this.move()
|
||||
this.center()
|
||||
this.isLoading(true)
|
||||
ffc.fetchMetadata().then(() => {
|
||||
this.isLoading(false)
|
||||
m.redraw(true)
|
||||
this.center()
|
||||
})
|
||||
}
|
||||
}
|
||||
this.move = () => {
|
||||
this.el().style.left = this.xpos() + 'px'
|
||||
this.el().style.top = this.ypos() + 'px'
|
||||
}
|
||||
this.center = () => {
|
||||
let rect = this.el().firstChild.getBoundingClientRect()
|
||||
this.xpos((window.innerWidth / 2) - (rect.width / 2) + document.body.scrollLeft)
|
||||
this.ypos((window.innerHeight / 2) - (rect.height / 2) + document.body.scrollTop)
|
||||
this.move()
|
||||
}
|
||||
|
||||
this.createEpub = (e) => {
|
||||
ffcProgress(0)
|
||||
ffcStatus('')
|
||||
e.target.disabled = true
|
||||
let chain = Promise.resolve()
|
||||
if (this.coverFile()) {
|
||||
chain = blobToArrayBuffer(this.coverFile()).then(ffc.setCoverImage.bind(ffc))
|
||||
ffc.coverUrl = ''
|
||||
ffc.coverImage = null
|
||||
if (this.checkboxCoverUrl()) {
|
||||
ffc.coverUrl = this.coverUrl()
|
||||
} else if (this.coverFile()) {
|
||||
chain = chain.then(blobToArrayBuffer.bind(null, this.coverFile())).then(ffc.setCoverImage.bind(ffc))
|
||||
}
|
||||
m.redraw()
|
||||
|
||||
|
@ -105,7 +125,7 @@ let dialog = {
|
|||
.then(ffc.getFile.bind(ffc)).then((file) => {
|
||||
console.log('Saving file...')
|
||||
if (typeof safari !== 'undefined') {
|
||||
blobToDataURL(file, (dataurl) => {
|
||||
blobToDataURL(file).then((dataurl) => {
|
||||
document.location.href = dataurl
|
||||
alert('Add .epub to the filename of the downloaded file')
|
||||
})
|
||||
|
@ -119,28 +139,27 @@ let dialog = {
|
|||
view (ctrl, args, extras) {
|
||||
return m('.drop-down-pop-up-container', {config: ctrl.onOpen.bind(ctrl)}, m('.drop-down-pop-up', [
|
||||
m('h1', {onmousedown: ctrl.ondown}, m('i.fa.fa-book'), 'Export to EPUB', m('a.close_button', {onclick: closeDialog})),
|
||||
m('.drop-down-pop-up-content', [
|
||||
ctrl.isLoading() ? m('div', {style: 'text-align:center;'}, m('i.fa.fa-spin.fa-spinner', {style: 'font-size:50px; margin:20px; color:#777;'})) : m('.drop-down-pop-up-content', [
|
||||
m('table.properties', [
|
||||
m('tr', m('td.label', 'Custom cover image'), m('td',
|
||||
// m(checkbox, {name: '', checked: true}, ' Custom cover'),
|
||||
// m('input', {type: 'url', placeholder: 'Image URL'}),
|
||||
// '- or -',
|
||||
m('form', [
|
||||
m('input', {type: 'file', accept: 'image/*', onchange: ctrl.setCoverFile}),
|
||||
m('button', {type: 'reset'}, 'Reset')
|
||||
])
|
||||
ctrl.checkboxCoverUrl() ? m('input', {type: 'url', placeholder: 'Image URL', onchange: m.withAttr('value', ctrl.coverUrl)}) : m('input', {type: 'file', accept: 'image/*', onchange: ctrl.setCoverFile})
|
||||
), m('td', m(checkbox, {checked: ctrl.checkboxCoverUrl(), onchange: m.withAttr('checked', ctrl.checkboxCoverUrl)}, 'Use image URL'))),
|
||||
m('tr', m('td.section_header', {colspan: 3}, m('b', 'Metadata'))),
|
||||
m('tr', m('td.label', 'Categories'), m('td', {colspan: 2},
|
||||
m('textarea', {rows: 5}, ctrl.subjects().join('\n')),
|
||||
m(checkbox, {checked: false}, 'Join categories into one (for iBooks)')
|
||||
))
|
||||
// m('tr', m('td.label', 'Chapter headings'), m('td', m(checkbox, {checked: true})))
|
||||
]),
|
||||
m('.drop-down-pop-up-footer', [
|
||||
m('button.styled_button', {onclick: ctrl.createEpub, disabled: ffcProgress() >= 0 && ffcProgress() < 1}, 'Create EPUB'),
|
||||
ffcProgress() >= 0 ? m('.rating_container',
|
||||
m('.bars_container', m('.bar_container', m('.bar_dislike', m('.bar.bar_like', {style: {width: ffcProgress() * 100 + '%'}})))),
|
||||
m('.rating_container',
|
||||
m('.bars_container', m('.bar_container', m('.bar_dislike', m('.bar.bar_like', {style: {width: Math.max(0, ffcProgress()) * 100 + '%'}})))),
|
||||
' ',
|
||||
ffcProgress() < 1 ? m('i.fa.fa-spin.fa-spinner') : null,
|
||||
ffcProgress() >= 0 && ffcProgress() < 1 ? m('i.fa.fa-spin.fa-spinner') : null,
|
||||
' ',
|
||||
ffcStatus()
|
||||
) : null
|
||||
)
|
||||
])
|
||||
])
|
||||
]))
|
||||
|
|
Loading…
Reference in a new issue