tidying up!

This commit is contained in:
daniel-j 2016-08-23 22:04:38 +02:00
parent 3e4d08f646
commit 4ae5731d14
3 changed files with 118 additions and 71 deletions

View file

@ -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;
}

View file

@ -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

View file

@ -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
)
])
])
]))