button to chapter comments, calculate word count (unused atm), clean up cleanup code

This commit is contained in:
daniel-j 2016-08-19 16:51:40 +02:00
parent d20ab34370
commit 9ad70f66b1
6 changed files with 148 additions and 91 deletions

View file

@ -14,19 +14,19 @@
"fimfic2epub": "./bin/fimfic2epub"
},
"main": "fimfic2epub.js",
"files": [
"fimfic2epub.js",
"fimfic2epub.js.map",
"bin/"
],
"dependencies": {
"detect-node": "^2.0.3",
"escape-string-regexp": "^1.0.5",
"html-entities": "^1.2.0",
"html-to-text": "^2.1.3",
"image-size": "^0.5.0",
"jszip": "^3.1.1",
"match-words": "^0.1.0",
"mithril": "^0.2.5",
"pretty-data": "^0.40.0",
"request": "^2.74.0",
@ -59,11 +59,12 @@
"webpack": "^2.1.0-beta.20",
"webpack-node-externals": "^1.3.3"
},
"standard": {
"env": {
"browser": true
},
"globals": ["FIMFIC2EPUB_VERSION"]
"globals": [
"FIMFIC2EPUB_VERSION"
]
}
}

View file

@ -211,7 +211,7 @@ module.exports = class FimFic2Epub {
}
this.tags = tags
cleanMarkup(description, (html) => {
cleanMarkup(description).then((html) => {
this.storyInfo.description = html
this.findRemoteResources('description', 'description', html)
resolve()

View file

@ -14,80 +14,86 @@ if (!isNode) {
tidy = require('tidy-html5').tidy_html5
}
export function cleanMarkup (html, callback) {
// fix center tags
html = html.replace(/<center>/g, '<p style="text-align: center;">')
html = html.replace(/<\/center>/g, '</p>')
export function cleanMarkup (html) {
if (!html) {
return Promise.resolve('')
}
// replace HTML non-breaking spaces with normal spaces
html = html.replace(/&nbsp;/g, ' ')
return new Promise((resolve, reject) => {
// 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(/&nbsp;/g, ' ')
html = fixDoubleSpacing(html)
html = fixParagraphIndent(html)
let cache = new Map()
let completeCount = 0
html = fixDoubleSpacing(html)
function getYoutubeInfo (ids) {
fetch('https://www.googleapis.com/youtube/v3/videos?id=' + ids + '&part=snippet&maxResults=50&key=' + youtubeKey, (raw, type) => {
let data
try {
data = JSON.parse(raw).items
} catch (e) {
let cache = new Map()
let completeCount = 0
}
data.forEach((video) => {
cache.set(video.id, video.snippet)
completeCount++
function getYoutubeInfo (ids) {
fetch('https://www.googleapis.com/youtube/v3/videos?id=' + ids + '&part=snippet&maxResults=50&key=' + youtubeKey, (raw, type) => {
let data
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
for (let ma; (ma = matchYoutube.exec(html));) {
let youtubeId = ma[1].match(/src="https:\/\/www.youtube.com\/embed\/(.+?)"/)[1]
cache.set(youtubeId, null)
}
let matchYoutube = /<div class="youtube_container">(.+?)<\/div>/g
for (let ma; (ma = matchYoutube.exec(html));) {
let youtubeId = ma[1].match(/src="https:\/\/www.youtube.com\/embed\/(.+?)"/)[1]
cache.set(youtubeId, null)
}
if (cache.size === 0) {
continueParsing()
} else {
getYoutubeInfo([...cache.keys()])
}
if (cache.size === 0) {
continueParsing()
} else {
getYoutubeInfo([...cache.keys()])
}
function continueParsing () {
html = html.replace(matchYoutube, (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
let title = 'Youtube Video'
let caption = ''
let data = cache.get(youtubeId)
if (data) {
thumbnail = (data.thumbnails.standard || data.thumbnails.high || data.thumbnails.medium || data.thumbnails.default).url
title = data.title
caption = data.title + ' on YouTube'
}
return render(m('figure.youtube', [
m('a', {href: youtubeUrl},
m('img', {src: thumbnail, alt: title})
),
m('figcaption', m('a', {href: youtubeUrl}, caption))
]))
})
function continueParsing () {
html = html.replace(matchYoutube, (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
let title = 'Youtube Video'
let caption = ''
let data = cache.get(youtubeId)
if (data) {
thumbnail = (data.thumbnails.standard || data.thumbnails.high || data.thumbnails.medium || data.thumbnails.default).url
title = data.title
caption = data.title + ' on YouTube'
}
return render(m('figure.youtube', [
m('a', {href: youtubeUrl},
m('img', {src: thumbnail, alt: title})
),
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-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-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 = tidy(`<?xml version="1.0" encoding="utf-8"?>\n` + html, tidyOptions)
html = tidy(html, tidyOptions).trim()
callback(html)
}
resolve(html)
}
})
}
export function fixDoubleSpacing (html) {

12
src/html-wordcount.js Normal file
View 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
}

View file

@ -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 {
ol {
list-style-type: none;

View file

@ -4,6 +4,7 @@ import render from './lib/mithril-node-render'
import { pd as pretty } from 'pretty-data'
import zeroFill from 'zero-fill'
import htmlWordCount from './html-wordcount'
import { cleanMarkup } from './cleanMarkup'
import { NS } from './constants'
@ -46,34 +47,44 @@ export function createChapter (ch, html, callback) {
// remove leading and trailing <br /> tags and whitespace
chapter = chapter.replace(trimWhitespace, '')
let sections = [
[
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
]
Promise.all([cleanMarkup(chapter), cleanMarkup(authorNotes)]).then((values) => {
let [cleanChapter, cleanAuthorNotes] = values
if (authorNotes && authorNotesPos < chapterPos) {
sections.reverse()
}
ch.realWordCount = htmlWordCount(cleanChapter)
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', sections)
])
)
let content = [
m.trust(cleanChapter),
cleanAuthorNotes ? m('div#author_notes', {className: authorNotesPos < chapterPos ? 'top' : 'bottom'}, [
m('p', m('b', 'Author\'s Note:')),
m.trust(cleanAuthorNotes)]) : null
]
cleanMarkup(chapterPage, (html) => {
callback(html)
// if author notes are a the beginning of the chapter
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)
})
}