mirror of
https://github.com/daniel-j/fimfic2epub.git
synced 2024-07-02 04:50:19 +12:00
button to chapter comments, calculate word count (unused atm), clean up cleanup code
This commit is contained in:
parent
d20ab34370
commit
9ad70f66b1
|
@ -14,19 +14,19 @@
|
||||||
"fimfic2epub": "./bin/fimfic2epub"
|
"fimfic2epub": "./bin/fimfic2epub"
|
||||||
},
|
},
|
||||||
"main": "fimfic2epub.js",
|
"main": "fimfic2epub.js",
|
||||||
|
|
||||||
"files": [
|
"files": [
|
||||||
"fimfic2epub.js",
|
"fimfic2epub.js",
|
||||||
"fimfic2epub.js.map",
|
"fimfic2epub.js.map",
|
||||||
"bin/"
|
"bin/"
|
||||||
],
|
],
|
||||||
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"detect-node": "^2.0.3",
|
"detect-node": "^2.0.3",
|
||||||
"escape-string-regexp": "^1.0.5",
|
"escape-string-regexp": "^1.0.5",
|
||||||
"html-entities": "^1.2.0",
|
"html-entities": "^1.2.0",
|
||||||
|
"html-to-text": "^2.1.3",
|
||||||
"image-size": "^0.5.0",
|
"image-size": "^0.5.0",
|
||||||
"jszip": "^3.1.1",
|
"jszip": "^3.1.1",
|
||||||
|
"match-words": "^0.1.0",
|
||||||
"mithril": "^0.2.5",
|
"mithril": "^0.2.5",
|
||||||
"pretty-data": "^0.40.0",
|
"pretty-data": "^0.40.0",
|
||||||
"request": "^2.74.0",
|
"request": "^2.74.0",
|
||||||
|
@ -59,11 +59,12 @@
|
||||||
"webpack": "^2.1.0-beta.20",
|
"webpack": "^2.1.0-beta.20",
|
||||||
"webpack-node-externals": "^1.3.3"
|
"webpack-node-externals": "^1.3.3"
|
||||||
},
|
},
|
||||||
|
|
||||||
"standard": {
|
"standard": {
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true
|
"browser": true
|
||||||
},
|
},
|
||||||
"globals": ["FIMFIC2EPUB_VERSION"]
|
"globals": [
|
||||||
|
"FIMFIC2EPUB_VERSION"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -211,7 +211,7 @@ module.exports = class FimFic2Epub {
|
||||||
}
|
}
|
||||||
this.tags = tags
|
this.tags = tags
|
||||||
|
|
||||||
cleanMarkup(description, (html) => {
|
cleanMarkup(description).then((html) => {
|
||||||
this.storyInfo.description = html
|
this.storyInfo.description = html
|
||||||
this.findRemoteResources('description', 'description', html)
|
this.findRemoteResources('description', 'description', html)
|
||||||
resolve()
|
resolve()
|
||||||
|
|
|
@ -14,80 +14,86 @@ if (!isNode) {
|
||||||
tidy = require('tidy-html5').tidy_html5
|
tidy = require('tidy-html5').tidy_html5
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cleanMarkup (html, callback) {
|
export function cleanMarkup (html) {
|
||||||
// fix center tags
|
if (!html) {
|
||||||
html = html.replace(/<center>/g, '<p style="text-align: center;">')
|
return Promise.resolve('')
|
||||||
html = html.replace(/<\/center>/g, '</p>')
|
}
|
||||||
|
|
||||||
// replace HTML non-breaking spaces with normal spaces
|
return new Promise((resolve, reject) => {
|
||||||
html = html.replace(/ /g, ' ')
|
// fix center tags
|
||||||
|
html = html.replace(/<center>/g, '<p style="text-align: center;">')
|
||||||
|
html = html.replace(/<\/center>/g, '</p>')
|
||||||
|
|
||||||
html = fixParagraphIndent(html)
|
// replace HTML non-breaking spaces with normal spaces
|
||||||
|
html = html.replace(/ /g, ' ')
|
||||||
|
|
||||||
html = fixDoubleSpacing(html)
|
html = fixParagraphIndent(html)
|
||||||
|
|
||||||
let cache = new Map()
|
html = fixDoubleSpacing(html)
|
||||||
let completeCount = 0
|
|
||||||
|
|
||||||
function getYoutubeInfo (ids) {
|
let cache = new Map()
|
||||||
fetch('https://www.googleapis.com/youtube/v3/videos?id=' + ids + '&part=snippet&maxResults=50&key=' + youtubeKey, (raw, type) => {
|
let completeCount = 0
|
||||||
let data
|
|
||||||
try {
|
|
||||||
data = JSON.parse(raw).items
|
|
||||||
} catch (e) {
|
|
||||||
|
|
||||||
}
|
function getYoutubeInfo (ids) {
|
||||||
data.forEach((video) => {
|
fetch('https://www.googleapis.com/youtube/v3/videos?id=' + ids + '&part=snippet&maxResults=50&key=' + youtubeKey, (raw, type) => {
|
||||||
cache.set(video.id, video.snippet)
|
let data
|
||||||
completeCount++
|
try {
|
||||||
|
data = JSON.parse(raw).items
|
||||||
|
} catch (e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
data.forEach((video) => {
|
||||||
|
cache.set(video.id, video.snippet)
|
||||||
|
completeCount++
|
||||||
|
})
|
||||||
|
if (completeCount === cache.size) {
|
||||||
|
continueParsing()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
if (completeCount === cache.size) {
|
}
|
||||||
continueParsing()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let matchYoutube = /<div class="youtube_container">(.+?)<\/div>/g
|
let matchYoutube = /<div class="youtube_container">(.+?)<\/div>/g
|
||||||
for (let ma; (ma = matchYoutube.exec(html));) {
|
for (let ma; (ma = matchYoutube.exec(html));) {
|
||||||
let youtubeId = ma[1].match(/src="https:\/\/www.youtube.com\/embed\/(.+?)"/)[1]
|
let youtubeId = ma[1].match(/src="https:\/\/www.youtube.com\/embed\/(.+?)"/)[1]
|
||||||
cache.set(youtubeId, null)
|
cache.set(youtubeId, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cache.size === 0) {
|
if (cache.size === 0) {
|
||||||
continueParsing()
|
continueParsing()
|
||||||
} else {
|
} else {
|
||||||
getYoutubeInfo([...cache.keys()])
|
getYoutubeInfo([...cache.keys()])
|
||||||
}
|
}
|
||||||
|
|
||||||
function continueParsing () {
|
function continueParsing () {
|
||||||
html = html.replace(matchYoutube, (match, contents) => {
|
html = html.replace(matchYoutube, (match, contents) => {
|
||||||
// console.log(match, contents)
|
// console.log(match, contents)
|
||||||
let youtubeId = contents.match(/src="https:\/\/www.youtube.com\/embed\/(.+?)"/)[1]
|
let youtubeId = contents.match(/src="https:\/\/www.youtube.com\/embed\/(.+?)"/)[1]
|
||||||
let thumbnail = 'http://img.youtube.com/vi/' + youtubeId + '/hqdefault.jpg'
|
let thumbnail = 'http://img.youtube.com/vi/' + youtubeId + '/hqdefault.jpg'
|
||||||
let youtubeUrl = 'https://youtube.com/watch?v=' + youtubeId
|
let youtubeUrl = 'https://youtube.com/watch?v=' + youtubeId
|
||||||
let title = 'Youtube Video'
|
let title = 'Youtube Video'
|
||||||
let caption = ''
|
let caption = ''
|
||||||
let data = cache.get(youtubeId)
|
let data = cache.get(youtubeId)
|
||||||
if (data) {
|
if (data) {
|
||||||
thumbnail = (data.thumbnails.standard || data.thumbnails.high || data.thumbnails.medium || data.thumbnails.default).url
|
thumbnail = (data.thumbnails.standard || data.thumbnails.high || data.thumbnails.medium || data.thumbnails.default).url
|
||||||
title = data.title
|
title = data.title
|
||||||
caption = data.title + ' on YouTube'
|
caption = data.title + ' on YouTube'
|
||||||
}
|
}
|
||||||
return render(m('figure.youtube', [
|
return render(m('figure.youtube', [
|
||||||
m('a', {href: youtubeUrl},
|
m('a', {href: youtubeUrl},
|
||||||
m('img', {src: thumbnail, alt: title})
|
m('img', {src: thumbnail, alt: title})
|
||||||
),
|
),
|
||||||
m('figcaption', m('a', {href: youtubeUrl}, caption))
|
m('figcaption', m('a', {href: youtubeUrl}, caption))
|
||||||
]))
|
]))
|
||||||
})
|
})
|
||||||
|
|
||||||
html = html.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">')
|
html = html.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">')
|
||||||
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">')
|
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">')
|
||||||
|
|
||||||
html = tidy(`<?xml version="1.0" encoding="utf-8"?>\n` + html, tidyOptions)
|
html = tidy(html, tidyOptions).trim()
|
||||||
|
|
||||||
callback(html)
|
resolve(html)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fixDoubleSpacing (html) {
|
export function fixDoubleSpacing (html) {
|
||||||
|
|
12
src/html-wordcount.js
Normal file
12
src/html-wordcount.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import htmlToText from 'html-to-text'
|
||||||
|
import matchWords from 'match-words'
|
||||||
|
|
||||||
|
export default function htmlWordCount (html) {
|
||||||
|
let text = htmlToText.fromString(html, {
|
||||||
|
wordwrap: false,
|
||||||
|
ignoreImage: true,
|
||||||
|
ignoreHref: true
|
||||||
|
})
|
||||||
|
|
||||||
|
return matchWords(text).length
|
||||||
|
}
|
|
@ -145,6 +145,33 @@ figure.youtube {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.chaptercomments {
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
margin: 1em;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
textcolor(#fff);
|
||||||
|
font-weight: normal;
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 0.9em;
|
||||||
|
text-shadow: -1px -1px #699739;
|
||||||
|
|
||||||
|
box-shadow: 0px 1px #81b945 inset;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid #699739;
|
||||||
|
border-bottom-color: #638f36;
|
||||||
|
border-top-color: #6fa03c;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
bgcolor(#7bb042);
|
||||||
|
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #7bb042), color-stop(100%, #6fa03c));
|
||||||
|
background: -webkit-linear-gradient(top, #7bb042 0%, #6fa03c 100%);
|
||||||
|
background: linear-gradient(to bottom, #7bb042 0%, #6fa03c 100%);
|
||||||
|
}
|
||||||
|
|
||||||
#toc {
|
#toc {
|
||||||
ol {
|
ol {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
|
|
|
@ -4,6 +4,7 @@ import render from './lib/mithril-node-render'
|
||||||
import { pd as pretty } from 'pretty-data'
|
import { pd as pretty } from 'pretty-data'
|
||||||
import zeroFill from 'zero-fill'
|
import zeroFill from 'zero-fill'
|
||||||
|
|
||||||
|
import htmlWordCount from './html-wordcount'
|
||||||
import { cleanMarkup } from './cleanMarkup'
|
import { cleanMarkup } from './cleanMarkup'
|
||||||
import { NS } from './constants'
|
import { NS } from './constants'
|
||||||
|
|
||||||
|
@ -46,34 +47,44 @@ export function createChapter (ch, html, callback) {
|
||||||
// remove leading and trailing <br /> tags and whitespace
|
// remove leading and trailing <br /> tags and whitespace
|
||||||
chapter = chapter.replace(trimWhitespace, '')
|
chapter = chapter.replace(trimWhitespace, '')
|
||||||
|
|
||||||
let sections = [
|
Promise.all([cleanMarkup(chapter), cleanMarkup(authorNotes)]).then((values) => {
|
||||||
[
|
let [cleanChapter, cleanAuthorNotes] = values
|
||||||
m('.chapter-title', [
|
|
||||||
m('h1', ch.title),
|
|
||||||
m('hr')
|
|
||||||
]),
|
|
||||||
m.trust(chapter)
|
|
||||||
],
|
|
||||||
authorNotes ? m('div#author_notes', {className: authorNotesPos < chapterPos ? 'top' : 'bottom'}, m.trust(authorNotes)) : null
|
|
||||||
]
|
|
||||||
|
|
||||||
if (authorNotes && authorNotesPos < chapterPos) {
|
ch.realWordCount = htmlWordCount(cleanChapter)
|
||||||
sections.reverse()
|
|
||||||
}
|
|
||||||
|
|
||||||
let chapterPage = '<!doctype html>' + render(
|
let content = [
|
||||||
m('html', {xmlns: NS.XHTML}, [
|
m.trust(cleanChapter),
|
||||||
m('head', [
|
cleanAuthorNotes ? m('div#author_notes', {className: authorNotesPos < chapterPos ? 'top' : 'bottom'}, [
|
||||||
m('meta', {charset: 'utf-8'}),
|
m('p', m('b', 'Author\'s Note:')),
|
||||||
m('link', {rel: 'stylesheet', type: 'text/css', href: '../Styles/style.css'}),
|
m.trust(cleanAuthorNotes)]) : null
|
||||||
m('title', ch.title)
|
]
|
||||||
]),
|
|
||||||
m('body', sections)
|
|
||||||
])
|
|
||||||
)
|
|
||||||
|
|
||||||
cleanMarkup(chapterPage, (html) => {
|
// if author notes are a the beginning of the chapter
|
||||||
callback(html)
|
if (cleanAuthorNotes && authorNotesPos < chapterPos) {
|
||||||
|
content.reverse()
|
||||||
|
}
|
||||||
|
|
||||||
|
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: '../Styles/style.css'}),
|
||||||
|
m('title', ch.title)
|
||||||
|
]),
|
||||||
|
m('body', [
|
||||||
|
m('.chapter-title', [
|
||||||
|
m('h1', ch.title),
|
||||||
|
m('hr')
|
||||||
|
]),
|
||||||
|
content,
|
||||||
|
m('p.double', {style: 'text-align: center; clear: both;'},
|
||||||
|
m('a.chaptercomments', {href: ch.link + '#comment_list'}, 'Read chapter comments online')
|
||||||
|
)
|
||||||
|
])
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
callback(chapterPage)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue