From e4e571f59bdc833c3af6c3a4964039d88edb07b8 Mon Sep 17 00:00:00 2001 From: Niklas Gollenstede Date: Sun, 21 Apr 2019 16:02:51 +0200 Subject: [PATCH] README/UI/docs, publish for Chrome, remove older reader mode workaround --- README.md | 36 ++++++-- background/index.js | 19 ++-- background/reader-mode.js | 83 ------------------ build-config.js | 7 +- common/options.js | 2 +- content/collect/about-reader.js | 6 +- content/collect/overdrive.js | 4 +- content/collect/readability.js | 6 +- package.json | 10 ++- .../chrome-web-store-screenshot-640x400.png | Bin 0 -> 18432 bytes resources/get-chrome-ext-206x58.png | Bin 0 -> 1528 bytes resources/get-firefox-ext-172x60.png | Bin 0 -> 2333 bytes 12 files changed, 61 insertions(+), 112 deletions(-) delete mode 100644 background/reader-mode.js create mode 100644 resources/chrome-web-store-screenshot-640x400.png create mode 100644 resources/get-chrome-ext-206x58.png create mode 100644 resources/get-firefox-ext-172x60.png diff --git a/README.md b/README.md index 4877ebe..2ebb6de 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,39 @@ -# ePub Creator -- create ePubs from web pages and `overdrive.com`s online reader +# ePub Creator -- offline e-books in a single click - + + + + ePub creator allows you to save web pages opened in the browser as offline ePub e-books. It currently supports: -Instructions: +How to create/save books:
    -
  1. open the page / book you want to save (on overdrive, go to "Loans" and choose "Read now in browser")
  2. +
  3. open the web page or book you want to save
      +
    • on overdrive, go to "Loans" and choose "Read now in browser"
    • +
  4. click the extensions icon (blue book with green arrow, should be at the top right of the browser, see screenshot)
  5. -
  6. wait while the animation on the icon is spinning (this can take a while in the e-book contains many pictures etc.)
  7. +
  8. wait while the animation on the icon is spinning
      +
    • this can take a while if the e-book contains many pictures etc.
    • +
    • for Reader Mode pages, it will prompt for the books author
    • +
  9. save or open the e-book when prompted
-Whether saving content this way is legal or not depends on the content and your local legislation. Checking this is your own responsibility. Just because you can do it doesn't mean you should. +Whether saving content with this extension is legal or not depends on the content and your local legislation. Checking that is your own responsibility. Just because you can do it doesn't mean you should. + + +What you get & Troubleshooting: + + Permissions used: + ## Development builds -- ![](https://ci.appveyor.com/api/projects/status/github/NiklasGollenstede/epub-creator?svg=true) @@ -52,7 +70,7 @@ The `.zip` file is ready to be uploaded on AMO, and the `build/` directory or th To test the extension in a fresh Firefox profile, use the `{run:1}` or `{run:{bin:'path/to firefox/binary'}}` option. -To build for chrome (which e.g. doesn't support `.svg` icons), add the `{chrome:1}` option. +To build for Chrome (which e.g. doesn't support `.svg` icons), add the `{chrome:1}` option. ## AMO code review notes diff --git a/background/index.js b/background/index.js index 17362dd..5ad06e3 100644 --- a/background/index.js +++ b/background/index.js @@ -1,5 +1,5 @@ (function(global) { 'use strict'; define(({ // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - 'node_modules/web-ext-utils/browser/': { BrowserAction, manifest, }, + 'node_modules/web-ext-utils/browser/': { BrowserAction, Tabs, manifest, }, 'node_modules/web-ext-utils/loader/': { runInFrame, }, 'node_modules/web-ext-utils/update/': updated, 'node_modules/web-ext-utils/utils/notify': notify, @@ -12,16 +12,23 @@ async function onClicked(tab) { return spinner.run(async () => { let collector, name = null; if (tab.isInReaderMode) { - name = (await (await require.async('./reader-mode'))(tab.url)); - } else if (/^https:\/\/[^/]*read\.overdrive\.com/.test(tab.url)) { + notify({ + title: `Exit reader mode?`, icon: 'default', // 'prompt', + message: `${manifest.name} can only save this page after closing the reader mode.\nClick here if you want leave reader mode.\nThen try again.`, + }).then(async clicked => + clicked && (await Tabs.get(tab.id)).isInReaderMode && Tabs.toggleReaderMode(tab.id) + ); return; + } else if ((/^https:[/][/][^/]*read[.]overdrive[.]com/).test(tab.url)) { collector = 'overdrive'; - } else if (tab.isArticle) { + } else if (tab.isArticle || !('isArticle' in tab)) { collector = 'readability'; } else { notify({ title: `Force reader mode?`, icon: 'warn', message: `It seems ${manifest.name} doesn't support this page.\nClick here if you want to TRY to force a book from reader mode.`, - }).then(async clicked => clicked && onClicked({ isArticle: true, id: tab.id, })); return; + }).then(async clicked => + clicked && onClicked({ isArticle: true, id: tab.id, }) + ); return; } collector && (name = (await runInFrame(tab.id, 0, @@ -31,7 +38,7 @@ async function onClicked(tab) { return spinner.run(async () => { if (name) { console.info(`Saved book "${name}"`); } else { console.info(`Saving as book was aborted`); } -}).catch(notify.error); } +}).catch(notify.error.bind(null, 'Failed to save as ePub')); } const spinner = { active: 0, strings: String.raw`\ | / –`.split(' '), diff --git a/background/reader-mode.js b/background/reader-mode.js deleted file mode 100644 index d3e7164..0000000 --- a/background/reader-mode.js +++ /dev/null @@ -1,83 +0,0 @@ -(function(global) { 'use strict'; define(({ // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - 'node_modules/web-ext-utils/browser/': { Tabs, Sessions, Windows, manifest, }, - 'node_modules/web-ext-utils/loader/views': { openView, }, - 'shim!node_modules/readability/Readability:Readability': Readability, -}) => async url => { /* globals fetch, */ - -/** - * Can't access pages in reader mode (yet?), so this emulates the reader mode - * in an extension view to transparently run content scripts in it. - */ - -// normalize url and start document download -// TODO: should grab the DOM of the rendered page (after 'load' event?) -url = url.replace(/^about:reader\?url=(.*)$/, (_, url) => decodeURIComponent(url)); -const getHtml = fetch(url).then(_=>_.text()); - -// prepare a fake content page (needs to be visible to load) -const { view, tabId, windowId, } = (await openView( - 'reader-mode-fix', 'popup', { width: 500, height: 500, }, // must be large enough for prompt() -)); try { - - // remove this popup from the window history once closed - windowId && Sessions && Windows && Windows.onRemoved.addListener(async function forget(closedId) { - if (closedId !== windowId) { return; } Windows.onRemoved.removeListener(forget); - const session = (await Sessions.getRecentlyClosed({ maxResults: 1, }))[0]; - session && session.window && Sessions.forgetClosedWindow(session.window.sessionId); - }); - - // add "UI" - view.document.head.innerHTML = ` -`; - view.document.body.innerHTML = ` -

Loading ...
Please keep this open.

`; - - // load the module loader - (await new Promise((callback, errback) => { - view.require = { callback, errback, }; - const script = view.document.createElement('script'); script.onerror = errback; - script.src = 'node_modules/pbq/require.js?baseUrl=/'; - view.document.head.appendChild(script); - })); - const { require, } = view, collector = 'about-reader'; - - // try to parse the fetched HTML - const parsed = new Readability(new view.DOMParser().parseFromString((await getHtml), 'text/html'), { }).parse(); - if (!parsed) { const error = new Error(` -The version of the reader mode included with ${manifest.name} was unable to parse this article. -This can happen for pages that load their content dynamically. -Please close the reader mode and try again! - `.trim()); error.title = 'Page could not be parsed'; throw error; } - - // build the reader mode DOM content - const document = new view.DOMParser().parseFromString(` - -
-
- $uri.host -

$title

-
$credits
-
-
$content
-
`, 'text/html'); - document.querySelector('.reader-domain').href = url || ''; - document.querySelector('.reader-domain').textContent = (new URL(url).host || '').replace(/^www\./, ''); - document.querySelector('.reader-title').textContent = parsed.title || ''; - document.querySelector('.reader-credits').textContent = parsed.byline || ''; - document.querySelector('.container>.content').innerHTML = parsed.content; // this is not a live document, so this should be unproblematic: https://github.com/mozilla/readability/issues/404 - - // and now we can pretend that we are in a content script on `about:reader?url=${url}`: - const name = (await require.async('content/collect').then(_=>_(collector, { document, }))); - - view.document.body.innerHTML = name ? `

Done!
Please close this window once the ePub is saved.

` : `

Canceled!
Please close this window.

`; - return name || false; -} catch (error) { - Tabs.remove(tabId); - throw error; -} - -}); })(this); diff --git a/build-config.js b/build-config.js index f571448..9028009 100644 --- a/build-config.js +++ b/build-config.js @@ -1,11 +1,14 @@ /*eslint strict: ["error", "global"], no-implicit-globals: "off"*/ 'use strict'; /* globals module, */ // license: MPL-2.0 -module.exports = function({ options, /*packageJson,*/ manifestJson, files, }) { +module.exports = function({ options, packageJson, manifestJson, files, }) { + + manifestJson.description = `Create offline e-books from web pages and 'overdrive.com' with a single click on the icon.`; + manifestJson.homepage_url = packageJson.homepage; manifestJson.permissions.push( 'notifications', 'sessions', // to remove own closed popups 'activeTab', - '', // for fetch in background/reader-mode.js + '', // required to fetch DORS stuff ); manifestJson.browser_action = { diff --git a/common/options.js b/common/options.js index 63d3db9..ca75b23 100644 --- a/common/options.js +++ b/common/options.js @@ -6,7 +6,7 @@ const model = { setNavProperty: { title: `Set 'nav' property`, - description: `Standard compliant when set, but disables the navigation in Sumatra PDF`, + description: `Standard compliant when set, but disables the navigation in Sumatra PDF.`, default: false, input: { type: 'boolean', }, }, diff --git a/content/collect/about-reader.js b/content/collect/about-reader.js index 3398226..5c7f522 100644 --- a/content/collect/about-reader.js +++ b/content/collect/about-reader.js @@ -7,7 +7,7 @@ const doc = document.querySelector('.container').cloneNode(true); -const resources = (await Promise.all(Array.map(doc.querySelectorAll('img'), async img => { +const resources = (await Promise.all(Array.from(doc.querySelectorAll('img'), async img => { const { src, } = img, name = img.src = (await sha1(src)) +'/'+ src.match(/[^/]*[/]?(?:[?]|#|$)/)[0]; return { src, name, }; }))); @@ -19,8 +19,8 @@ const author = global.prompt('Please enter/confirm the authors name', ( ).textContent.replace(/\s+/g, ' ') || ''); if (author == null) { return null; } -Array.forEach(doc.querySelectorAll('style, link, menu'), element => element.remove()); -Array.forEach(doc.querySelectorAll('*'), element => { +doc.querySelectorAll('style, link, menu').forEach(element => element.remove()); +doc.querySelectorAll('*').forEach(element => { for (let i = element.attributes.length; i-- > 0;) { const attr = element.attributes[i]; if ([ 'class', 'src', 'href', 'title', 'alt', ].includes(attr.name)) { continue; } diff --git a/content/collect/overdrive.js b/content/collect/overdrive.js index 48591e8..b7e7dd9 100644 --- a/content/collect/overdrive.js +++ b/content/collect/overdrive.js @@ -56,7 +56,7 @@ const chapters = (await Promise.all(bData.spine.map(async ({ .italic { font-style: italic; } .underline { text-decoration: underline; } .bold { font-weight: bold; } -` ); + `); options.styles && (css += Array.from(styles, ([ style, index, ]) => `\t\t.inline-${index} { ${style} }\n`).join('')); // html clean-up @@ -91,7 +91,7 @@ const chapters = (await Promise.all(bData.spine.map(async ({ return ({ name: name && decodeURI(name) || 'unnamed'+ index +'.html', title, - content: toXML(document).replace(/^.*$/m, ``), + content: toXML(document).replace(/^.*?>/, `\n\n`), mimeType: 'xhtml', // toXML produces xhtml linear, }); diff --git a/content/collect/readability.js b/content/collect/readability.js index 742f578..31a9bb6 100644 --- a/content/collect/readability.js +++ b/content/collect/readability.js @@ -3,10 +3,12 @@ 'shim!node_modules/readability/Readability:Readability': Readability, aboutReader, }) => async function collect({ document: srcDoc = global.document.cloneNode(true), url = srcDoc.URL, } = { }) { - /** - * Can't access pages in reader mode any more, so this emulates the reader mode + * Collects the contents of any website that can be opened in the `about:reader` view. + * Since extensions can't access pages in reader mode any more, this emulates the reader mode * in the original content page and then proceeds as if it was loaded in `about:reader`. + * (This workaround is kept for now to be able to switch back to the reader view if it ever becomes accessible again.) + * @return {object} Options that can be passed as argument to the EPub constructor. */ const parsed = new Readability(srcDoc, { }).parse(); diff --git a/package.json b/package.json index 981f4c3..32bbb47 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "epub", - "version": "0.5.4", + "version": "0.5.5", "title": "ePub Creator", "description": "ePub Creator is a WebExtension that creates ePub e-books from web pages and 'overdrive.com's online reader.", "author": "Niklas Gollenstede", @@ -9,6 +9,8 @@ "type": "git", "url": "https://github.com/NiklasGollenstede/epub-creator" }, + "homepage": "https://github.com/NiklasGollenstede/epub-creator#readme", + "keywords": [ "book", "e-book", "ePub", "website", "article", "save", "offline", "one click", "overdrive.com", "reader mode" ], "contributions": [ { "what": { "name": "Readability.js", "url": "https://github.com/mozilla/readability" }, "who": [ "Mozilla", "Arc90" ], @@ -26,10 +28,10 @@ "es6lib": "0.0.3", "jszip": "github:Stuk/jszip#v3.1.5", "multiport": "0.2.3", - "pbq": "0.3.5", - "readability": "github:mozilla/readability#d8c8370", + "pbq": "0.4.1", + "readability": "github:mozilla/readability#26379fe", "web-ext-build": "0.0.10", - "web-ext-utils": "0.1.8" + "web-ext-utils": "0.1.11" }, "devDependencies": { "eslintrc": "github:NiklasGollenstede/eslintrc#5837452", diff --git a/resources/chrome-web-store-screenshot-640x400.png b/resources/chrome-web-store-screenshot-640x400.png new file mode 100644 index 0000000000000000000000000000000000000000..51847144bcebe0349b72d04ed11a8dad02369166 GIT binary patch literal 18432 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 bM@s$=F+3Y}75y2K00000NkvXXu0mjfXGt&@ literal 0 HcmV?d00001 diff --git a/resources/get-chrome-ext-206x58.png b/resources/get-chrome-ext-206x58.png new file mode 100644 index 0000000000000000000000000000000000000000..c541262f01184d636dc16252383b13f9215c80f6 GIT binary patch literal 1528 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 Duf}Yt literal 0 HcmV?d00001