added Safari extension support

This commit is contained in:
Daniel J 2016-06-22 11:54:37 +02:00
parent 9aa9c1e1a1
commit 0ad2d0c82a
8 changed files with 205 additions and 60 deletions

3
.gitignore vendored
View file

@ -1,5 +1,8 @@
.DS_Store
node_modules/
extension/fimfic2epub.js
extension/eventPage.js
extension.crx
extension.pem
extension.xpi
fimfic2epub.safariextension/

56
extension/Info.plist Normal file
View file

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Author</key>
<string>djazz</string>
<key>Builder Version</key>
<string>11601.5.17.1</string>
<key>CFBundleDisplayName</key>
<string>fimfic2epub</string>
<key>CFBundleIdentifier</key>
<string>com.yourcompany.fimfic2epub</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>Chrome</key>
<dict>
<key>Global Page</key>
<string>global.html</string>
</dict>
<key>Content</key>
<dict>
<key>Scripts</key>
<dict>
<key>End</key>
<array>
<string>fimfic2epub.js</string>
</array>
</dict>
<key>Whitelist</key>
<array>
<string>http://www.fimfiction.net/story/*</string>
<string>https://www.fimfiction.net/story/*</string>
</array>
</dict>
<key>DeveloperIdentifier</key>
<string>0000000000</string>
<key>ExtensionInfoDictionaryVersion</key>
<string>1.0</string>
<key>Permissions</key>
<dict>
<key>Website Access</key>
<dict>
<key>Include Secure Pages</key>
<true/>
<key>Level</key>
<string>All</string>
</dict>
</dict>
<key>Website</key>
<string>https://github.com/daniel-j/fimfic2epub</string>
</dict>
</plist>

View file

@ -1,29 +0,0 @@
/* global chrome */
'use strict'
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 () {
console.log(x.getResponseHeader('content-type'))
cb(URL.createObjectURL(x.response), x.getResponseHeader('content-type'))
}
x.onerror = function () {
console.error('error')
cb(null)
}
x.send()
}
let onMessage = chrome.extension.onMessage ? chrome.extension.onMessage : chrome.runtime.onMessage
onMessage.addListener(function (request, sender, sendResponse) {
fetch(request, sendResponse, 'blob')
return true
})

10
extension/global.html Normal file
View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title>Background page</title>
</head>
<body>
<script type="text/javascript" src="eventPage.js"></script>
</body>
</html>

View file

@ -67,12 +67,11 @@ function webpackTask (callback) {
}
let lintPipe = lazypipe()
.pipe(filter, ['**/*', '!extension/fimfic2epub.js'])
.pipe(standard)
.pipe(standard.reporter, 'default', { breakOnError: false })
// Cleanup task
gulp.task('clean', () => del('extension/fimfic2epub.js'))
gulp.task('clean', () => del(['extension/fimfic2epub.js', 'extension/eventPage.js']))
// Main tasks
gulp.task('webpack', webpackTask)
@ -83,7 +82,7 @@ gulp.task('watch:webpack', () => {
})
gulp.task('lint', () => {
return gulp.src(['gulpfile.babel.js', 'webpack.config.babel.js', 'src/**/*.js', 'extension/**/*.js']).pipe(lintPipe())
return gulp.src(['gulpfile.babel.js', 'webpack.config.babel.js', 'src/**/*.js']).pipe(lintPipe())
})
gulp.task('watch:lint', () => {
return watch(['src/**/*.js'], watchOpts, function (file) {

44
src/eventPage.js Normal file
View file

@ -0,0 +1,44 @@
/* global chrome, safari */
'use strict'
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()
}
if (typeof safari !== 'undefined') {
safari.application.addEventListener('message', function (ev) {
let url = ev.message
fetch(url, function (buffer, type) {
console.log('Fetched ' + url + ' (' + type + ')')
ev.target.page.dispatchMessage('remote', {
input: url,
output: buffer,
type: type
})
}, 'arraybuffer')
}, false)
} else {
let onMessage = chrome.extension.onMessage ? chrome.extension.onMessage : chrome.runtime.onMessage
onMessage.addListener(function (request, sender, sendResponse) {
fetch(request, function (blob, type) {
sendResponse(URL.createObjectURL(blob), type)
}, 'blob')
return true
})
}

View file

@ -1,4 +1,4 @@
/* global chrome */
/* global chrome, safari */
'use strict'
import JSZip from 'jszip'
@ -95,7 +95,7 @@ function fetchChapters (cb) {
function recursive () {
let ch = chapters[currentChapter]
console.log('Fetching chapter ' + ch.id + ' ' + ch.title)
fetch(ch.link.replace('http', 'https'), function (html) {
fetchRemote(ch.link.replace('http', 'https'), function (html) {
html = parseChapter(ch, html)
chapterContent[ch.id] = html
currentChapter++
@ -109,7 +109,62 @@ function fetchChapters (cb) {
recursive()
}
function fetchRemote (zip, cb) {
let safariQueue = {}
function safariHandler (ev) {
let type = ev.message.type
let url = ev.message.input
let data = ev.message.output // arraybuffer
if (!safariQueue[url]) {
// console.error("Unable to get callback for " + url, JSON.stringify(safariQueue))
return
}
let cb = safariQueue[url].cb
let responseType = safariQueue[url].responseType
console.log(url, cb, responseType, data)
delete safariQueue[url]
if (responseType === 'blob') {
let blob = new Blob([data], {type: type})
cb(blob, type)
} else {
if (!responseType) {
let blob = new Blob([data], {type: type})
let fr = new FileReader()
fr.onloadend = function () {
cb(fr.result, type)
}
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 {
cb(data, type)
}
}
}
if (typeof safari !== 'undefined') {
safari.self.addEventListener('message', safariHandler, false)
}
function fetchRemote (url, cb, responseType) {
if (typeof chrome !== 'undefined' && chrome.runtime.sendMessage) {
chrome.runtime.sendMessage(url, function (objurl) {
fetch(objurl, cb, responseType)
URL.revokeObjectURL(objurl)
})
} else {
safariQueue[url] = {cb: cb, responseType: responseType}
safari.self.tab.dispatchMessage('remote', url)
}
}
function fetchRemoteFiles (zip, cb) {
let iter = remoteResources.entries()
let counter = 0
@ -122,26 +177,18 @@ function fetchRemote (zip, cb) {
let url = r[0]
r = r[1]
console.log('Fetching remote file ' + r.filename, url)
chrome.runtime.sendMessage(url, function (objUrl) {
if (objUrl) {
fetch(objUrl, function (data, type) {
r.dest = null
r.type = type
let dest = mimeMap[type]
fetchRemote(url, function (data, type) {
r.dest = null
r.type = type
let dest = mimeMap[type]
if (dest) {
r.dest = dest.replace('*', r.filename)
zip.file(r.dest, data)
}
URL.revokeObjectURL(objUrl)
counter++
recursive()
}, 'arraybuffer')
} else {
counter++
recursive()
if (dest) {
r.dest = dest.replace('*', r.filename)
zip.file(r.dest, data)
}
})
counter++
recursive()
}, 'arraybuffer')
}
recursive()
}
@ -161,7 +208,7 @@ function downloadStory () {
console.log('Fetching story...')
fetch(apiUrl, function (raw) {
fetchRemote(apiUrl, function (raw, type) {
let data
try {
data = JSON.parse(raw)
@ -185,7 +232,7 @@ function downloadStory () {
zip.file('nav.xhtml', createNav())
fetchChapters(function () {
fetchRemote(zip, function () {
fetchRemoteFiles(zip, function () {
remoteResources.forEach((r, url) => {
if (r.chapter && r.originalUrl && r.dest) {
chapterContent[r.chapter] = chapterContent[r.chapter].replace(
@ -223,7 +270,7 @@ function downloadStory () {
})
*/
console.log('packing epub...')
console.log('Packaging epub...')
zip
.generateAsync({
@ -243,8 +290,22 @@ function downloadStory () {
})
}
function blobToDataURL (blob, callback) {
let a = new FileReader()
a.onloadend = function (e) { callback(a.result) }
a.readAsDataURL(blob)
}
function saveStory () {
saveAs(cachedBlob, storyInfo.title + ' by ' + storyInfo.author.name + '.epub')
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 (ch, html) {
@ -320,7 +381,7 @@ function parseChapter (ch, html) {
}
function subjects (s) {
var list = []
let list = []
for (let i = 0; i < s.length; i++) {
list.push(m('dc:subject', s[i]))
}
@ -383,7 +444,7 @@ function createOpf () {
}
function navPoints (list) {
var arr = []
let arr = []
for (let i = 0; i < list.length; i++) {
list[i]
arr.push(m('navPoint', {id: 'navPoint-' + (i + 1), playOrder: i + 1}, [

View file

@ -6,7 +6,8 @@ let inProduction = process.env.NODE_ENV === 'production' || process.argv.indexOf
export default {
entry: {
fimfic2epub: ['./src/main']
fimfic2epub: ['./src/main'],
eventPage: ['./src/eventPage']
},
output: {
path: path.join(__dirname, '/'),