diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index ecec59f..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-node_modules/
-tests/pages/
-.DS_Store
\ No newline at end of file
diff --git a/README.md b/README.md
index 17fec2e..85411e2 100644
--- a/README.md
+++ b/README.md
@@ -2,17 +2,13 @@
Save a web page/selection as an eBook (.epub format) - a Chrome/Firefox/Opera Web Extension
-
+

-
-
## How to install it
-### From [Chrome Web Store](https://chrome.google.com/webstore/detail/save-as-ebook/haaplkpoiimngbppjihnegfmpejdnffj)
-
-or manually (tested on v. 52.0.2743.116)
+### Chrome (tested on v. 52.0.2743.116)
```
1. Navigate to chrome://extensions/
@@ -20,9 +16,7 @@ or manually (tested on v. 52.0.2743.116)
3. Select the extension's directory
```
-### From [Firefox Add-ons](https://addons.mozilla.org/en-US/firefox/addon/saveasebook/)
-
-or manually (tested on v. 50.0a2)
+### Firefox (tested on v. 50.0a2)
```
1. Navigate to about:debugging
@@ -45,91 +39,19 @@ sudo apt-get install calibre
ebook-convert "book.epub" "book.mobi"
```
-## Default Keyboard Shortcuts
-
-**NOTE** These shortcuts are not fixed and the browser will assign a different shortcut if the default one is taken
-
-| Shortcut | Description |
-| --- | --- |
-| Alt + Shift + 1 | Save current page as eBook |
-| Alt + Shift + 2 | Save current selection as eBook |
-| Alt + Shift + 3 | Add current page as chapter |
-| Alt + Shift + 4 | Add current selection as chapter |
-
-## How to change the default Shortcuts
-
-in Chrome:
-
-```
-1. Navigate to chrome://extensions/
-2. Scroll down
-3. Click on Keyboard shortcuts
-```
-
-## Added in 1.4.2
- - Added MIME type to the generated .epub file
-
-## Added in 1.4.1
- - Remove unnecessary permissions
- - Detect image type if the URL doesn't have a file extension (jpg, gif, png)
- - Reset the Busy indicator on errors
- - Remove hidden elements when style is not included
- - Replace iframes with divs
-
-## Added in 1.4
- - Smaller ebook file size
- - Fix for #37 - custom styles not applied
- - Fix for #36 - br tag missing from pre blocks
- - Fix for #31 - hanging in Busy state
- - Other misc bug fixes
-
-## Added in v1.3.4
- - Fix for MathML - the rendered expression is too large (Issue #26)
- - Add translation in Russian (thanks to @ Emil Khalikov) & Brazilian Portuguese (thanks to @welksonramos)
-
-## Added in v1.3
- - Keyboard shortcuts
- - Simplified tool bar menu
- - Misc bug fixes
-
-## Added in v1.2.2
- - fixed & & issue in title; Issue # 10
-
-## Added in v1.2.1
- - support for hr/br html tags
-
-## Added in v1.2
- - BETA: Support for CSS
- - BETA: Create / edit custom Styles
- - No errors from EPUB Validator (http://validator.idpf.org/) + this should fix the Google Play upload issue
-
-## Added in v1.1
- - Chapter Editor: option to save changes
- - Chapter Editor: option to remove all chapters
- - persist Chapter Editor changes & chapters after generating an eBook or after a browser restart
-
## To-Do
- - make the Custom Style Editor more user friendly
- - support backup / restore for Custom Styles
- - DONE fix all 'epubcheck' errors (https://github.com/IDPF/epubcheck)
+ - fix all 'epubcheck' errors (https://github.com/IDPF/epubcheck)
+ - * there are some issues with relative URLs
- clean & optimize code
- create tests
+ - convert svg to png
+ - base64 imgs
+ - 'save as image' option (render a selection and save it as image instead of text)
- support other formats (mobi, pdf etc.)
+ - show progress indication (ui/ux)
- show confirmations (ui/ux)
- display errors (ui/ux)
- - DONE support custom style
- - add 'remove from ebook' right click menu action
-
-## Run Tests (Work in progress...)
- ```
- cd tests
- yarn install # install puppeteer
- node test/index.js # should start a chrome instance with Save as eBook loaded
-
- # it will generate and save the ebook in ./tmp-downloads
-
- ....
- ```
+ - add settings & options page (ui/ux)
## Credits
- http://ebooks.stackexchange.com/questions/1183/what-is-the-minimum-required-content-for-a-valid-epub
@@ -137,5 +59,4 @@ in Chrome:
- https://stuk.github.io/jszip/
- http://johnny.github.io/jquery-sortable/
- https://github.com/eligrey/FileSaver.js/
- - https://www.iconfinder.com/icons/753890/book_books_education_library_study_icon#size=128
- - Thanks to [pumpk0n](https://github.com/pumpk0n) and [Francois Bocquet](https://github.com/fbocquet) for helping me with the French translation
+ - https://www.iconfinder.com/icons/1031371/book_empty_library_reading_icon#size=128
diff --git a/ex1.png b/ex1.png
new file mode 100644
index 0000000..f6de59c
Binary files /dev/null and b/ex1.png differ
diff --git a/ex11.png b/ex11.png
deleted file mode 100644
index 3d60c53..0000000
Binary files a/ex11.png and /dev/null differ
diff --git a/ex2.png b/ex2.png
index 8967c0d..375eb8f 100644
Binary files a/ex2.png and b/ex2.png differ
diff --git a/ex3.png b/ex3.png
deleted file mode 100644
index 88a153e..0000000
Binary files a/ex3.png and /dev/null differ
diff --git a/tests/index.js b/tests/index.js
deleted file mode 100644
index 6988655..0000000
--- a/tests/index.js
+++ /dev/null
@@ -1,52 +0,0 @@
-const puppeteer = require('puppeteer');
-const fs = require('fs')
-
-const CRX_PATH = '../web-extension';
-const REFERENCE_EBOOK_PATH = 'reference-ebook'
-const TEST_RESULT_EBOOK_PATH = 'test-result-ebook'
-const TEST_EBOOK_FILE_NAME = 'test.epub'
-
-const testPaths = [
-
-]
-
-puppeteer.launch({
- headless: false,
- args: [
- `--disable-extensions-except=${CRX_PATH}`,
- `--load-extension=${CRX_PATH}`,
- '--user-agent=PuppeteerTestingAgent'
- ]
-}).then(async browser => {
-
- prepareTests()
-
- await runLocalFullPageTests(browser)
-});
-
-async function runLocalFullPageTests(browser) {
-
- const testedFileName = 'mathjax' //'canvas' //'svg' //'special-chars' // 'p2' //'p1'
-
- const testUrl = 'file://'+__dirname+'/pages/'+testedFileName+'/page/index.html'
- const resultDownloadPath = './pages/'+testedFileName+'/' + TEST_RESULT_EBOOK_PATH
-
- const page = await browser.newPage();
- await page._client.send('Page.setDownloadBehavior', {behavior: 'allow', downloadPath: resultDownloadPath});
- await page.setViewport({ width: 1280, height: 800 })
- // await page.goto('https://en.wikipedia.org/wiki/E-book', { waitUntil: 'networkidle0' });
- // await page.goto('file://'+__dirname+'/../pages/p1/E-book - Wikipedia.html', { waitUntil: 'networkidle0' });
- await page.goto(testUrl, { waitUntil: 'networkidle0' });
-}
-
-function prepareTests() {
- const testedFileName = 'p1'
- const pathToDelete = './pages/'+testedFileName+'/'+TEST_RESULT_EBOOK_PATH+'/'+TEST_EBOOK_FILE_NAME
- try {
- if (fs.existsSync(pathToDelete)) {
- fs.unlinkSync(pathToDelete)
- }
- } catch(err) {
- console.log('Error while deleting file ', err);
- }
-}
\ No newline at end of file
diff --git a/tests/package.json b/tests/package.json
deleted file mode 100644
index f4adacd..0000000
--- a/tests/package.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "name": "web-extension-test",
- "version": "1.0.0",
- "main": "index.js",
- "license": "MIT",
- "dependencies": {
- "puppeteer": "^1.14.0"
- }
-}
diff --git a/tests/pages.zip b/tests/pages.zip
deleted file mode 100644
index 7e5ecce..0000000
Binary files a/tests/pages.zip and /dev/null differ
diff --git a/web-extension/_locales/en/messages.json b/web-extension/_locales/en/messages.json
deleted file mode 100644
index 951c629..0000000
--- a/web-extension/_locales/en/messages.json
+++ /dev/null
@@ -1,94 +0,0 @@
-{
- "extName": {
- "message": "Save as eBook",
- "description": "Extension name"
- },
- "includeStyle": {
- "message": "Include (Custom) Style"
- },
- "editStyles": {
- "message": "Edit Custom Styles ..."
- },
- "savePage": {
- "message": "Save Page"
- },
- "saveSelection": {
- "message": "Save Selection"
- },
- "pageChapter": {
- "message": "Add Page as Chapter"
- },
- "selectionChapter": {
- "message": "Add Selection as Chapter"
- },
- "editChapters": {
- "message": "Edit Chapters ..."
- },
- "waitMessage": {
- "message": "Please Wait ..."
- },
- "chapterEditorTitle": {
- "message": "Chapter Editor"
- },
- "ebookTitleLabel": {
- "message": "eBook Title:"
- },
- "rawPreview": {
- "message": "Raw Preview"
- },
- "remove": {
- "message": "Remove"
- },
- "removeChapters": {
- "message": "Remove Chapters"
- },
- "removeChaptersConfirm": {
- "message": "Do you want to remove all chapters?"
- },
- "cancel": {
- "message": "Cancel"
- },
- "generateEbook": {
- "message": "Generate eBook ..."
- },
- "saveChanges": {
- "message": "Save changes"
- },
- "emptyBookWarning": {
- "message": "Can't generate an empty eBook!"
- },
- "confirmDeleteStyle": {
- "message": "Are you sure you want to delete this style?"
- },
- "invalidRegex": {
- "message": "Invalid regular expression"
- },
- "styleSaved": {
- "message": "Style saved!"
- },
- "styleEditor": {
- "message": "Style Editor"
- },
- "selectExistingCSS": {
- "message": "Select Existing CSS"
- },
- "orLabel": {
- "message": "or"
- },
- "createNewStyle": {
- "message": "Create New Style"
- },
- "styleNameLabel": {
- "message": "Name"
- },
- "howToWriteRegexLabel": {
- "message": "How to write a regular expression pattern"
- },
- "removeStyle": {
- "message": "Remove Style"
- },
- "saveStyle": {
- "message": "Save Style"
- }
-
-}
diff --git a/web-extension/_locales/fr/messages.json b/web-extension/_locales/fr/messages.json
deleted file mode 100644
index 5366e29..0000000
--- a/web-extension/_locales/fr/messages.json
+++ /dev/null
@@ -1,99 +0,0 @@
-{
- "extName": {
- "message": "Enregistrer comme eBook",
- "description": "Nom de l'extension"
- },
- "includeStyle": {
- "message": "Inclure le style"
- },
- "styleLabel": {
- "message": "Style personnalisé :"
- },
- "applyStyle": {
- "message": "Appliquer"
- },
- "editStyles": {
- "message": "Éditer les styles..."
- },
- "savePage": {
- "message": "Enregistrer la page"
- },
- "saveSelection": {
- "message": "Enregistrer la sélection"
- },
- "pageChapter": {
- "message": "Ajouter la page en tant que chapitre"
- },
- "selectionChapter": {
- "message": "Ajouter la sélection en tant que chapitre"
- },
- "editChapters": {
- "message": "Modifier les chapitres..."
- },
- "waitMessage": {
- "message": "Merci de patienter..."
- },
- "chapterEditorTitle": {
- "message": "Éditeur de chapitre"
- },
- "ebookTitleLabel": {
- "message": "Titre de l'eBook :"
- },
- "rawPreview": {
- "message": "Aperçu brut"
- },
- "remove": {
- "message": "Retirer"
- },
- "removeChapters": {
- "message": "Supprimer les chapitres"
- },
- "removeChaptersConfirm": {
- "message": "Voulez-vous supprimer tous les chapitres ?"
- },
- "cancel": {
- "message": "Annuler"
- },
- "generateEbook": {
- "message": "Générer l'eBook..."
- },
- "saveChanges": {
- "message": "Enregistrer les modifications"
- },
- "emptyBookWarning": {
- "message": "Impossible de générer un eBook vide !"
- },
- "confirmDeleteStyle": {
- "message": "Êtes-vous sûr de vouloir supprimer ce style ?"
- },
- "invalidRegex": {
- "message": "Expression régulière invalide"
- },
- "styleSaved": {
- "message": "Style enregistré !"
- },
- "styleEditor": {
- "message": "Éditeur de style"
- },
- "selectExistingCSS": {
- "message": "Sélectionner un style existant"
- },
- "orLabel": {
- "message": "ou"
- },
- "createNewStyle": {
- "message": "Créer un nouveau style"
- },
- "styleNameLabel": {
- "message": "Nom"
- },
- "howToWriteRegexLabel": {
- "message": "Comment écrire une expression régulière"
- },
- "removeStyle": {
- "message": "Supprimer le style"
- },
- "saveStyle": {
- "message": "Enregistrer le style"
- }
-}
diff --git a/web-extension/_locales/pt_BR/messages.json b/web-extension/_locales/pt_BR/messages.json
deleted file mode 100644
index 9c59f7c..0000000
--- a/web-extension/_locales/pt_BR/messages.json
+++ /dev/null
@@ -1,93 +0,0 @@
-{
- "extName": {
- "message": "Salvar como eBook",
- "description": "Nome da extensão"
- },
- "includeStyle": {
- "message": "Incuir estilo (personalizado)"
- },
- "editStyles": {
- "message": "Editar estilos personalizados ..."
- },
- "savePage": {
- "message": "Salvar Página"
- },
- "saveSelection": {
- "message": "Salvar Selecão"
- },
- "pageChapter": {
- "message": "Adicionar página como capítulo"
- },
- "selectionChapter": {
- "message": "Adicionar seleção como capítulo"
- },
- "editChapters": {
- "message": "Editar Capítulos ..."
- },
- "waitMessage": {
- "message": "Por favor, aguarde ..."
- },
- "chapterEditorTitle": {
- "message": "Editor de capítulos"
- },
- "ebookTitleLabel": {
- "message": "Título do eBook:"
- },
- "rawPreview": {
- "message": "Pré-visualização"
- },
- "remove": {
- "message": "Remover"
- },
- "removeChapters": {
- "message": "Remover capítulos"
- },
- "removeChaptersConfirm": {
- "message": "Você quer remover todos os capítulos?"
- },
- "cancel": {
- "message": "Cancelar"
- },
- "generateEbook": {
- "message": "Gerar eBook ..."
- },
- "saveChanges": {
- "message": "Salvar alterações"
- },
- "emptyBookWarning": {
- "message": "Não é possível gerar um eBook vazio!"
- },
- "confirmDeleteStyle": {
- "message": "Tem certeza de que deseja excluir este estilo?"
- },
- "invalidRegex": {
- "message": "Expressão regular inválida"
- },
- "styleSaved": {
- "message": "Estilo salvo!"
- },
- "styleEditor": {
- "message": "Editor de estilo"
- },
- "selectExistingCSS": {
- "message": "Selecionar CSS Existente"
- },
- "orLabel": {
- "message": "ou"
- },
- "createNewStyle": {
- "message": "Criar Novo Estilo"
- },
- "styleNameLabel": {
- "message": "Nome"
- },
- "howToWriteRegexLabel": {
- "message": "Como escrever um padrão de expressão regular"
- },
- "removeStyle": {
- "message": "Remover Estilo"
- },
- "saveStyle": {
- "message": "Salvar Estilo"
- }
-}
\ No newline at end of file
diff --git a/web-extension/_locales/ru/messages.json b/web-extension/_locales/ru/messages.json
deleted file mode 100644
index 7726ba9..0000000
--- a/web-extension/_locales/ru/messages.json
+++ /dev/null
@@ -1,93 +0,0 @@
-{
- "extName": {
- "message": "Save as eBook",
- "description": "Extension name"
- },
- "includeStyle": {
- "message": "Подключить CSS"
- },
- "editStyles": {
- "message": "Редактировать CSS ..."
- },
- "savePage": {
- "message": "Сохранить страницу"
- },
- "saveSelection": {
- "message": "Сохранить выделение"
- },
- "pageChapter": {
- "message": "Добавить страницу как главу"
- },
- "selectionChapter": {
- "message": "Добавить выделение как главу"
- },
- "editChapters": {
- "message": "Редактировать главы ..."
- },
- "waitMessage": {
- "message": "Пожалуйста ожидайте ..."
- },
- "chapterEditorTitle": {
- "message": "Редактор глав"
- },
- "ebookTitleLabel": {
- "message": "Заголовок книги:"
- },
- "rawPreview": {
- "message": "Предпросмотр"
- },
- "remove": {
- "message": "Удалить"
- },
- "removeChapters": {
- "message": "Удалить главы"
- },
- "removeChaptersConfirm": {
- "message": "Вы точно хотите удалить все главы?"
- },
- "cancel": {
- "message": "Отмена"
- },
- "generateEbook": {
- "message": "Создать книгу ..."
- },
- "saveChanges": {
- "message": "Сохранить изменения"
- },
- "emptyBookWarning": {
- "message": "Невозможно создать пустую книгу"
- },
- "confirmDeleteStyle": {
- "message": "Вы точно хотите удалить этот стиль?"
- },
- "invalidRegex": {
- "message": "Ошибка в регулярном выражении"
- },
- "styleSaved": {
- "message": "Стиль сохранен!"
- },
- "styleEditor": {
- "message": "Редактор стилей"
- },
- "selectExistingCSS": {
- "message": "Выбрать существующий стиль"
- },
- "orLabel": {
- "message": "или"
- },
- "createNewStyle": {
- "message": "Создать новый стиль"
- },
- "styleNameLabel": {
- "message": "Название"
- },
- "howToWriteRegexLabel": {
- "message": "Введите регулярное выражение для адреса"
- },
- "removeStyle": {
- "message": "Удалить стиль"
- },
- "saveStyle": {
- "message": "Сохранить стиль"
- }
-}
\ No newline at end of file
diff --git a/web-extension/background.js b/web-extension/background.js
index a577260..60c2fba 100644
--- a/web-extension/background.js
+++ b/web-extension/background.js
@@ -1,429 +1,44 @@
+var customStorage = null;
-///////////////////
-///////////////////
-///////////////////
-///////////////////
-/// Only for testing
-
-chrome.runtime.onInstalled.addListener(details => {
- if (navigator.userAgent === 'PuppeteerTestingAgent') {
- let TEST_TIMER = null
- chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
- if (TEST_TIMER) {
- clearTimeout(TEST_TIMER)
- }
-
- TEST_TIMER = setTimeout(()=> {
- executeCommand({type: 'save-page'})
- }, 2000)
- });
- }
-});
-
-
-
-///////////////////
-///////////////////
-///////////////////
-///////////////////
-
-var isBusy = false;
-var busyResetTimer = null
-
-var defaultStyles = [
- {
- title: 'Reddit Comments',
- url: 'reddit\\.com\\/r\\/[^\\/]+\\/comments',
- style: `.side {
-display: none;
-}
-#header {
-display: none;
-}
-.arrow, .expand, .score, .live-timestamp, .flat-list, .buttons, .morecomments, .footer-parent, .icon {
-display: none !important;
-}
-`
- },{
- title: 'Wikipedia Article',
- url: 'wikipedia\\.org\\/wiki\\/',
- style: `#mw-navigation {
-display: none;
-}
-#footer {
-display: none;
-}
-#mw-panel {
-display: none;
-}
-#mw-head {
-display: none;
-}
-`
- },{
- title: 'YCombinator News Comments',
- url: 'news\\.ycombinator\\.com\\/item\\?id=[0-9]+',
- style: `#hnmain > tbody > tr:nth-child(1) > td > table {
-display: none;
-}
-* {
-background-color: white;
-}
-.title, .storylink {
-text-align: left;
-font-weight: bold;
-font-size: 20px;
-}
-.score {
-display: none;
-}
-.age {
-display: none;
-}
-.hnpast {
-display: none;
-}
-.togg {
-display: none;
-}
-.votelinks, .rank {
-display: none;
-}
-.votearrow {
-display: none;
-}
-.yclinks {
-display: none;
-}
-form {
-display: none;
-}
-a.hnuser {
-font-weight: bold;
-color: black !important;
-padding: 3px;
-}
-.subtext > span, .subtext > a:not(:nth-child(2)) {
-display: none;
-}
-`
- },{
- title: 'Medium Article',
- url: 'medium\\.com',
- style: `.metabar {
-display: none !important;
-}
-header.container {
-display: none;
-}
-.js-postShareWidget {
-display: none;
-}
-footer, canvas {
-display: none !important;
-}
-.u-fixed, .u-bottom0 {
-display: none;
-}
-`
- },{
- title: 'Twitter',
- url: 'twitter\\.com\\/.+',
- style: `.topbar {
-display: none !important;
-}
-.ProfileCanopy, .ProfileCanopy-inner {
-display: none;
-}
-.ProfileSidebar {
-display: none;
-}
-.ProfileHeading {
-display: none !important;
-}
-.ProfileTweet-actionList {
-display: none;
-}
-`
- }
-
-];
-
-chrome.commands.onCommand.addListener((command) => {
- executeCommand({type: command})
-});
-
-function executeCommand(command) {
- if (isBusy) {
- chrome.tabs.query({
- currentWindow: true,
- active: true
- }, (tab) => {
- chrome.tabs.sendMessage(tab[0].id, {'alert': 'Work in progress! Please wait until the current eBook is generated!'}, (r) => {
- console.log(r);
- });
- })
- return;
- }
- if (command.type === 'save-page') {
- dispatch('extract-page', false, []);
- } else if (command.type === 'save-selection') {
- dispatch('extract-selection', false, []);
- } else if (command.type === 'add-page') {
- dispatch('extract-page', true, []);
- } else if (command.type === 'add-selection') {
- dispatch('extract-selection', true, []);
- }
-
- isBusy = true
-
- //
- busyResetTimer = setTimeout(() => {
- resetBusy()
- }, 20000)
-}
-
-function dispatch(action, justAddToBuffer, appliedStyles) {
- if (!justAddToBuffer) {
- _execRequest({type: 'remove'});
- }
- chrome.browserAction.setBadgeBackgroundColor({color:"red"});
- chrome.browserAction.setBadgeText({text: "Busy"});
-
- chrome.tabs.query({
- currentWindow: true,
- active: true
- }, (tab) => {
-
- isIncludeStyles((result) =>{
- let isIncludeStyle = result.includeStyle
- prepareStyles(tab, isIncludeStyle, appliedStyles, (tmpAppliedStyles) => {
- applyAction(tab, action, justAddToBuffer, isIncludeStyle, tmpAppliedStyles, () => {
- alert('done')
- })
- })
- })
- });
-}
-
-function isIncludeStyles(callback) {
- chrome.storage.local.get('includeStyle', (data) => {
- if (!data) {
- callback({includeStyle: false});
+function _getEbookPages() {
+ try {
+ // var allPages = localStorage.getItem('ebook');
+ var allPages = customStorage;
+ if (!allPages) {
+ allPages = [];
} else {
- callback({includeStyle: data.includeStyle});
+ allPages = JSON.parse(allPages);
}
- });
-}
-
-function prepareStyles(tab, includeStyle, appliedStyles, callback) {
- if (!includeStyle) {
- callback(appliedStyles)
- return
- }
-
- chrome.storage.local.get('styles', (data) => {
- let styles = defaultStyles;
- if (data && data.styles) {
- styles = data.styles;
- }
- let currentUrl = tab[0].url;
- let currentStyle = null;
-
- if (!styles) {
- callback(appliedStyles)
- return
- }
-
- if (styles.length === 0) {
- callback(appliedStyles)
- return
- }
-
- let allMatchingStyles = [];
-
- for (let i = 0; i < styles.length; i++) {
- currentUrl = currentUrl.replace(/(http[s]?:\/\/|www\.)/i, '').toLowerCase();
- let styleUrl = styles[i].url;
- let styleUrlRegex = null;
-
- try {
- styleUrlRegex = new RegExp(styleUrl, 'i');
- } catch (e) {
- }
-
- if (styleUrlRegex && styleUrlRegex.test(currentUrl)) {
- allMatchingStyles.push({
- index: i,
- length: styleUrl.length
- });
- }
- }
-
- if (allMatchingStyles.length === 0) {
- callback(appliedStyles)
- return
- }
-
- allMatchingStyles.sort((a, b) => b.length - a.length);
- let selStyle = allMatchingStyles[0];
-
- if (!selStyle) {
- callback(appliedStyles)
- return
- }
-
- currentStyle = styles[selStyle.index];
-
- if (!currentStyle) {
- callback(appliedStyles)
- return
- }
-
- if (!currentStyle.style) {
- callback(appliedStyles)
- return
- }
-
- chrome.tabs.insertCSS(tab[0].id, { code: currentStyle.style }, () => {
- appliedStyles.push(currentStyle);
- callback(appliedStyles)
- });
- });
-}
-
-function applyAction(tab, action, justAddToBuffer, includeStyle, appliedStyles, callback) {
- chrome.tabs.sendMessage(tab[0].id, {
- type: action,
- includeStyle: includeStyle,
- appliedStyles: appliedStyles
- }, (response) => {
-
- if (!response) {
- resetBusy()
- chrome.tabs.sendMessage(tab[0].id, {'alert': 'Save as eBook does not work on this web site!'}, (r) => {});
- return;
- }
-
- if (response.content.trim() === '') {
- resetBusy()
- if (justAddToBuffer) {
- chrome.tabs.sendMessage(tab[0].id, {'alert': 'Cannot add an empty selection as chapter!'}, (r) => {});
- } else {
- chrome.tabs.sendMessage(tab[0].id, {'alert': 'Cannot generate the eBook from an empty selection!'}, (r) => {});
- }
- return;
- }
- if (!justAddToBuffer) {
- chrome.tabs.sendMessage(tab[0].id, {'shortcut': 'build-ebook', response: [response]}, (r) => {});
- } else {
- chrome.storage.local.get('allPages', (data) => {
- if (!data || !data.allPages) {
- data.allPages = [];
- }
- data.allPages.push(response);
- chrome.storage.local.set({'allPages': data.allPages});
- resetBusy()
- chrome.tabs.sendMessage(tab[0].id, {'alert': 'Page or selection added as chapter!'}, (r) => {});
- })
- }
- });
-}
-
-function resetBusy() {
- isBusy = false
-
- if (busyResetTimer) {
- clearTimeout(busyResetTimer)
- busyResetTimer = null
- }
-
- chrome.browserAction.setBadgeText({text: ""})
-
- let popups = chrome.extension.getViews({type: "popup"});
- if (popups && popups.length > 0) {
- popups[0].close()
+ return allPages;
+ } catch (e) {
+ alert(e);
+ return [];
}
}
-chrome.runtime.onMessage.addListener(_execRequest);
+function _saveEbookPages(pages) {
+ try {
+ // localStorage.setItem('ebook', JSON.stringify(pages));
+ customStorage = JSON.stringify(pages);
+ } catch (e) {
+ alert(e);
+ }
+}
-function _execRequest(request, sender, sendResponse) {
+function _removeEbook() {
+ // localStorage.removeItem('ebook');
+ customStorage = null;
+}
+
+chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.type === 'get') {
- chrome.storage.local.get('allPages', function (data) {
- if (!data || !data.allPages) {
- sendResponse({allPages: []});
- }
- sendResponse({allPages: data.allPages});
- })
+ sendResponse({allPages: _getEbookPages()});
}
if (request.type === 'set') {
- chrome.storage.local.set({'allPages': request.pages});
+ _saveEbookPages(request.pages);
}
if (request.type === 'remove') {
- chrome.storage.local.remove('allPages');
- chrome.storage.local.remove('title');
- }
- if (request.type === 'get title') {
- chrome.storage.local.get('title', function (data) {
- if (!data || !data.title || data.title.trim().length === 0) {
- sendResponse({title: 'eBook'});
- } else {
- sendResponse({title: data.title});
- }
- })
- }
- if (request.type === 'set title') {
- chrome.storage.local.set({'title': request.title});
- }
- if (request.type === 'get styles') {
- chrome.storage.local.get('styles', function (data) {
- if (!data || !data.styles) {
- sendResponse({styles: defaultStyles});
- } else {
- sendResponse({styles: data.styles});
- }
- });
- }
- if (request.type === 'set styles') {
- chrome.storage.local.set({'styles': request.styles});
- }
- if (request.type === 'get current style') {
- chrome.storage.local.get('currentStyle', function (data) {
- if (!data || !data.currentStyle) {
- sendResponse({currentStyle: 0});
- } else {
- sendResponse({currentStyle: data.currentStyle});
- }
- });
- }
- if (request.type === 'set current style') {
- chrome.storage.local.set({'currentStyle': request.currentStyle});
- }
- if (request.type === 'get include style') {
- chrome.storage.local.get('includeStyle', function (data) {
- if (!data) {
- sendResponse({includeStyle: false});
- } else {
- sendResponse({includeStyle: data.includeStyle});
- }
- });
- }
- if (request.type === 'set include style') {
- chrome.storage.local.set({'includeStyle': request.includeStyle});
- }
- if (request.type === 'is busy?') {
- sendResponse({isBusy: isBusy})
- }
- if (request.type === 'set is busy') {
- isBusy = request.isBusy
- }
- if (request.type === 'save-page' || request.type === 'save-selection' ||
- request.type === 'add-page' || request.type === 'add-selection') {
- executeCommand({type: request.type})
- }
- if (request.type === 'done') {
- resetBusy()
+ _removeEbook();
}
return true;
-}
+});
diff --git a/web-extension/chapterEditor.css b/web-extension/chapterEditor.css
index e3d42e2..86e9e57 100644
--- a/web-extension/chapterEditor.css
+++ b/web-extension/chapterEditor.css
@@ -12,13 +12,24 @@ ul.chapterEditor-chapters-list li.placeholder {
padding: 0;
border: solid 10px #f5f5f5;
}
+/*ul.chapterEditor-chapters-list li.placeholder:before {
+ position: absolute;
+ content: "";
+ margin-top: -5px;
+ left: -5px;
+ top: -4px;
+ border: 5px solid black;
+ border-left-color: red;
+ border-right: none;
+}*/
+
#chapterEditor-Title {
font-size: 20px;
font-weight: bold;
float: left;
display: inline-block;
- font-family: sans-serif;
+ font-family: "sans-serif";
}
#chapterEditor-ebookTitleHolder {
@@ -35,7 +46,7 @@ ul.chapterEditor-chapters-list li.placeholder {
#chapterEditor-ebookTitle {
padding: 5px;
font-size: 15px;
- font-family: sans-serif;
+ font-family: "sans-serif";
width: 85%;
}
ul, ul.chapterEditor-chapters-list {
@@ -66,7 +77,7 @@ ul, ul.chapterEditor-chapters-list {
.chapterEditor-chapter-item > input[type="text"] {
padding: 5px;
font-size: 15px;
- font-family: sans-serif;
+ font-family: "sans-serif";
width: 80%;
border: none;
border-bottom: solid 1px #aaa;
@@ -79,6 +90,9 @@ ul, ul.chapterEditor-chapters-list {
}
#chapterEditor-modalList {
display: block;
+ /*width: 100%;*/
+ /*border-top: solid 1px black;*/
+ /*border-bottom: solid 1px black;*/
padding: 20px;
padding-top: 0;
}
@@ -97,7 +111,7 @@ ul, ul.chapterEditor-chapters-list {
.chapterEditor-text-button {
border: none;
font-size: 15px;
- font-family: sans-serif;
+ font-family: "sans-serif";
padding: 5px;
background-color: rgba(0, 0, 0, 0);
margin: 0 3px;
@@ -120,48 +134,21 @@ ul, ul.chapterEditor-chapters-list {
}
.chapterEditor-footer-button {
- position: relative;
- overflow: hidden;
padding: 18px 20px;
margin: 0;
font-size: 18px;
border: none;
background-color: rgba(0, 0, 0, 0);
- display: inline-block;
}
.chapterEditor-footer-button:hover {
color: white;
background-color: rgba(0, 0, 0, 1);
cursor: pointer;
}
-.chapterEditor-footer-button:after {
- content: '';
- position: absolute;
- top: 50%;
- left: 50%;
- width: 5px;
- height: 5px;
- background: rgba(255, 255, 255, .5);
- opacity: 0;
- border-radius: 100%;
- transform: scale(1, 1) translate(-50%);
- transform-origin: 50% 50%;
-}
-@keyframes ripple {
- 0% { transform: scale( 0, 0); opacity: 1; }
- 20% { transform: scale(25, 25); opacity: 1; }
- 100% { transform: scale(40, 40); opacity: 0; }
-}
-.chapterEditor-footer-button:focus:not(:active)::after {
- animation: ripple 1s ease-out;
-}
-.chapterEditor-footer-button.chapterEditor-generate-button {
+.chapterEditor-generate-button {
background-color: yellow;
}
-.chapterEditor-footer-button.chapterEditor-generate-button:hover {
- background-color: rgba(0, 0, 0, 1);
-}
-.chapterEditor-footer-button.chapterEditor-cancel-button {
+.chapterEditor-cancel-button {
/*background-color: black;*/
}
diff --git a/web-extension/chapterEditor.js b/web-extension/chapterEditor.js
index d54e4f3..d91445a 100644
--- a/web-extension/chapterEditor.js
+++ b/web-extension/chapterEditor.js
@@ -26,7 +26,7 @@ function showEditor() {
// Header
var title = document.createElement('span');
title.id = "chapterEditor-Title";
- title.innerText = chrome.i18n.getMessage('chapterEditorTitle');
+ title.innerText = "Chapter Editor";
var upperCloseButton = document.createElement('button');
modalHeader.appendChild(title);
upperCloseButton.onclick = closeModal;
@@ -41,15 +41,13 @@ function showEditor() {
var ebookTilteLabel = document.createElement('span');
ebookTilteLabel.id = 'chapterEditor-ebookTitleLabel';
- ebookTilteLabel.innerText = chrome.i18n.getMessage('ebookTitleLabel');
+ ebookTilteLabel.innerText = 'eBook Title: ';
titleHolder.appendChild(ebookTilteLabel);
var ebookTilte = document.createElement('input');
ebookTilte.id = 'chapterEditor-ebookTitle';
ebookTilte.type = 'text';
- getEbookTitle(function (title) {
- ebookTilte.value = title;
- });
+ ebookTilte.value = 'eBook';
titleHolder.appendChild(ebookTilte);
modalList.appendChild(titleHolder);
@@ -81,12 +79,12 @@ function showEditor() {
var buttons = document.createElement('span');
var previewButton = document.createElement('button');
- previewButton.innerText = chrome.i18n.getMessage('rawPreview');
+ previewButton.innerText = 'Raw Preview';
previewButton.className = 'chapterEditor-text-button';
previewButton.onclick = previewListItem(i);
var removeButton = document.createElement('button');
- removeButton.innerText = chrome.i18n.getMessage('remove');
+ removeButton.innerText = 'Remove';
removeButton.className = 'chapterEditor-text-button chapterEditor-text-red';
removeButton.onclick = removeListItem(i);
@@ -108,40 +106,18 @@ function showEditor() {
// Footer
var buttons = document.createElement('div');
var closeButton = document.createElement('button');
- closeButton.innerText = chrome.i18n.getMessage('cancel');
+ closeButton.innerText = 'Cancel';
closeButton.className = 'chapterEditor-footer-button chapterEditor-float-left chapterEditor-cancel-button';
closeButton.onclick = closeModal;
buttons.appendChild(closeButton);
- var removeButton = document.createElement('button');
- removeButton.innerText = chrome.i18n.getMessage('removeChapters');
- removeButton.className = 'chapterEditor-footer-button hapterEditor-float-left';
- removeButton.onclick = function() {
- var result = confirm(chrome.i18n.getMessage('removeChaptersConfirm'));
- if (result) {
- removeEbook();
- closeModal();
- }
- };
- buttons.appendChild(removeButton);
-
var saveButton = document.createElement('button');
saveButton.onclick = function() {
- var newChapters = saveChanges();
- prepareEbook(newChapters);
+ prepareEbook();
};
- saveButton.innerText = chrome.i18n.getMessage('generateEbook');
+ saveButton.innerText = 'Generate eBook ...';
saveButton.className = 'chapterEditor-footer-button chapterEditor-float-right chapterEditor-generate-button';
buttons.appendChild(saveButton);
-
- var saveChangesButton = document.createElement('button');
- saveChangesButton.onclick = function() {
- saveChanges();
- };
- saveChangesButton.innerText = chrome.i18n.getMessage('saveChanges');
- saveChangesButton.className = 'chapterEditor-footer-button chapterEditor-float-right';
- buttons.appendChild(saveChangesButton);
-
modalFooter.appendChild(buttons);
/////////////////////
@@ -206,23 +182,11 @@ function showEditor() {
function previewListItem(atIndex) {
return function() {
- alert(allPagesRef[atIndex].content.trim().replace(/<[^>]+>/gi, '').replace(/\s+/g, ' ').substring(0, 1000) + ' ...');
+ alert(allPagesRef[atIndex].content.trim().substring(0, 500).replace(/<[^>]+>/gi, '') + ' ...');
};
}
- function prepareEbook(newChapters) {
- try {
- if (newChapters.length === 0) {
- alert(chrome.i18n.getMessage('emptyBookWarning'));
- return;
- }
- buildEbookFromChapters();
- } catch (e) {
- console.log('Error:', e);
- }
- }
-
- function saveChanges() {
+ function prepareEbook() {
var newChapters = [];
var newEbookTitle = ebookTilte.value;
if (newEbookTitle.trim() === '') {
@@ -230,27 +194,35 @@ function showEditor() {
}
try {
+
var tmpChaptersList = document.getElementsByClassName('chapterEditor-chapter-item');
if (!tmpChaptersList || !allPagesRef) {
return;
}
for (var i = 0; i < tmpChaptersList.length; i++) {
- var tmpChapterItem = tmpChaptersList[i];
- var listIndex = Number(tmpChapterItem.id.replace('li', ''));
+ var listIndex = Number(tmpChaptersList[i].id.replace('li', ''));
if (allPagesRef[listIndex].removed === false) {
- var newChapterTitle = tmpChapterItem.children.namedItem('text'+listIndex).value;
- allPagesRef[listIndex].title = newChapterTitle;
newChapters.push(allPagesRef[listIndex]);
}
}
- saveEbookTitle(newEbookTitle);
+ if (newChapters.length === 0) {
+ alert('Can\'t generate an empty eBook!');
+ return;
+ }
+
+ newChapters.splice(0, 0, {
+ type: 'title',
+ title: newEbookTitle
+ });
+
saveEbookPages(newChapters);
- return newChapters;
+ buildEbookFromChapters();
} catch (e) {
console.log('Error:', e);
}
+
}
/////////////////////
diff --git a/web-extension/cssEditor.css b/web-extension/cssEditor.css
deleted file mode 100644
index 88ed70f..0000000
--- a/web-extension/cssEditor.css
+++ /dev/null
@@ -1,190 +0,0 @@
-#cssEditor-removeStyle {
- display: none;
-}
-#cssEditor-saveStyle {
- display: none;
-}
-#cssEditor-selectStyle {
- padding: 3px;
- cursor: pointer;
- font-size: 15px;
- font-family: sans-serif;
-}
-#cssEditor-selectStyle > option {
- padding: 3px;
- font-size: 1em;
- cursor: pointer;
- font-size: 15px;
- font-family: sans-serif;
-}
-#cssEditor-orLabel {
- padding: 3px;
- margin: 0 3px;
- font-size: 15px;
- font-family: sans-serif;
-}
-#cssEditor-createNewStyle {
- padding: 3px;
- cursor: pointer;
- font-size: 15px;
- font-family: sans-serif;
-}
-.cssEditor-field-label-holder {
- margin-top: 10px;
- font-size: 15px;
- font-family: sans-serif;
- display: flex;
- justify-content: space-between;
- width: 90%;
-}
-.cssEditor-field-holder {
- padding: 3px;
- width: 90%;
-}
-.cssEditor-field-label {
- padding: 0 3px;
- margin-top: 5px;
-}
-
-#cssEditor-styleEditor {
- display: flex;
- flex-flow: row;
-}
-
-.cssEditor-left-panel {
- width: 48%;
- display: flex;
- flex-flow: column;
-}
-
-.cssEditor-right-panel {
- width: 48%;
- display: flex;
- flex-flow: column;
-}
-
-#cssEditor-styleName {
- width: 100%;
- min-width: 100%;
- padding: 5px;
- border: solid rgba(0, 0, 0, 0.25) 1px;
- font-size: 15px;
- font-family: sans-serif;
-}
-#cssEditor-matchUrl {
- width: 100%;
- min-width: 100%;
- padding: 5px;
- border: solid rgba(0, 0, 0, 0.25) 1px;
- font-size: 15px;
- font-family: sans-serif;
-}
-#cssEditor-styleContent {
- width: 100%;
- height: 150px;
- padding: 5px;
- border: solid rgba(0, 0, 0, 0.25) 1px;
- font-size: 15px;
- font-family: sans-serif;
-}
-
-#cssEditor-Title {
- font-size: 20px;
- font-weight: bold;
- float: left;
- display: inline-block;
- font-family: sans-serif;
-}
-
-#cssEditor-ebookTitleHolder {
- background-color: #eee;
- padding: 10px 20px;
- margin-bottom: 10px;
-}
-
-#cssEditor-modalHeader {
- display: block;
- overflow: hidden;
- padding: 20px;
-}
-#cssEditor-modalList {
- display: block;
- padding: 20px;
- padding-top: 0;
-}
-#cssEditor-modalFooter {
- display: block;
- overflow: hidden;
-}
-
-.cssEditor-text-button {
- border: none;
- font-size: 15px;
- font-family: sans-serif;
- padding: 5px;
- background-color: rgba(0, 0, 0, 0);
- margin: 0 3px;
- outline: none;
- cursor: pointer;
-}
-.cssEditor-text-button:hover {
- background-color: #000;
- color: #fff;
-}
-.cssEditor-text-red {
- color: red;
-}
-
-.cssEditor-float-left {
- float: left;
-}
-.cssEditor-float-right {
- float: right;
-}
-
-.cssEditor-footer-button {
- padding: 18px 20px;
- margin: 0;
- font-size: 18px;
- border: none;
- background-color: rgba(0, 0, 0, 0);
- display: inline-block;
-}
-.cssEditor-footer-button:hover {
- color: white;
- background-color: rgba(0, 0, 0, 1);
- cursor: pointer;
-}
-.cssEditor-save-button {
- background-color: yellow;
-}
-.cssEditor-cancel-button {
- /*background-color: black;*/
-}
-
-@media (max-width: 1700px) {
- .cssEditor-text-button {
- font-size: 12px;
- padding: 3px;
- margin: 0 2px;
- }
- .cssEditor-footer-button {
- padding: 15px 20px;
- margin: 0;
- font-size: 15px;
- border: none;
- background-color: rgba(0, 0, 0, 0);
- }
-}
-
-@media (max-width: 1100px) {
- .cssEditor-chapter-item {
- line-height: 30px;
- height: 60px;
- padding: 4px 0;
- font-size: 14px;
- }
- .cssEditor-chapter-item > input[type="text"] {
- width: 90%;
- }
-}
diff --git a/web-extension/cssEditor.js b/web-extension/cssEditor.js
deleted file mode 100644
index ea28993..0000000
--- a/web-extension/cssEditor.js
+++ /dev/null
@@ -1,379 +0,0 @@
-for (var i=0; i 0) {
- allStyles = allStyles.concat(allStylesTmp);
- }
-
- while (existingStyles.hasChildNodes() && existingStyles.childElementCount > 1) {
- existingStyles.removeChild(existingStyles.lastChild);
- }
-
- for (var i = 0; i < allStyles.length; i++) {
- var listItem = document.createElement('option');
- listItem.id = 'option_' + i;
- listItem.className = 'cssEditor-chapter-item';
- listItem.value = 'option_' + i;
- listItem.innerText = allStyles[i].title;
- if (currentStyle && (allStyles[i].title === currentStyle.title)) {
- listItem.selected = 'selected';
- }
- existingStyles.appendChild(listItem);
- }
- }
-
- function editCurrentStyle() {
- if (!currentStyle) {
- return;
- }
-
- showStyleEditor();
- showRemoveStyle();
- showSaveStyle();
-
- document.getElementById('cssEditor-styleName').value = currentStyle.title;
- document.getElementById('cssEditor-matchUrl').value = currentStyle.url;
- document.getElementById('cssEditor-styleContent').value = currentStyle.style;
-
- }
-
- function resetFields() {
- document.getElementById('cssEditor-styleName').value = '';
- document.getElementById('cssEditor-matchUrl').value = '';
- document.getElementById('cssEditor-styleContent').value = '';
- }
-
- function hideStyleEditor() {
- document.getElementById('cssEditor-styleEditor').style.display = 'none';
- }
-
- function showStyleEditor() {
- document.getElementById('cssEditor-styleEditor').style.display = 'flex';
- }
-
- function showRemoveStyle() {
- document.getElementById('cssEditor-removeStyle').style.display = 'inline-block';
- }
-
- function hideRemoveStyle() {
- document.getElementById('cssEditor-removeStyle').style.display = 'none';
- }
-
- function showSaveStyle() {
- document.getElementById('cssEditor-saveStyle').style.display = 'inline-block';
- }
-
- function hideSaveStyle() {
- document.getElementById('cssEditor-saveStyle').style.display = 'none';
- }
-
- function saveStyle() {
- var isRegexValid = checkRegex();
- if (!isRegexValid) {
- alert(chrome.i18n.getMessage('invalidRegex'));
- return;
- }
- var tmpValue = {
- title: document.getElementById('cssEditor-styleName').value,
- url: document.getElementById('cssEditor-matchUrl').value,
- style: document.getElementById('cssEditor-styleContent').value
- }
- if (currentStyle === null) {
- allStyles.push(tmpValue);
- currentStyle = tmpValue;
- currentStyleIndex = allStyles.length - 1;
- } else {
- currentStyle = tmpValue;
- allStyles[currentStyleIndex] = currentStyle;
- }
- setStyles(allStyles);
- createStyleList();
- showRemoveStyle();
- alert(chrome.i18n.getMessage('styleSaved'));
- }
-
- function checkRegex() {
- var regexContent = document.getElementById('cssEditor-matchUrl').value;
- var isValid = true;
- try {
- new RegExp(regexContent);
- } catch(e) {
- isValid = false;
- }
- return isValid;
- }
-
- function removeStyle() {
- if (confirm(chrome.i18n.getMessage('confirmDeleteStyle')) == true) {
- allStyles.splice(currentStyleIndex, 1);
- setStyles(allStyles);
- hideSaveStyle();
- hideRemoveStyle();
- hideStyleEditor();
- createStyleList();
- }
- }
-
-
- /////////////////////
-
- var modal = document.createElement('div');
- modal.id = 'cssEditor-Modal';
-
- modalContent.appendChild(modalHeader);
- modalContent.appendChild(modalList);
- modalContent.appendChild(modalFooter);
- modal.appendChild(modalContent);
-
- body.appendChild(modal);
-
- modal.style.display = "none";
- modal.style.position = 'fixed';
- modal.style.zIndex = '1';
- modal.style.left = '0';
- modal.style.top = '0';
- modal.style.width = '100%';
- modal.style.height = '100%';
- modal.style.overflow = 'auto';
- modal.style.backgroundColor = 'rgba(210, 210, 210, 1)';
-
- modalContent.style.zIndex = '2';
- modalContent.style.backgroundColor = '#fff';
- modalContent.style.margin = '5% auto';
- modalContent.style.padding = '0';
- modalContent.style.width = '70%';
-
- window.onclick = function(event) {
- if (event.target == modal) {
- closeModal();
- }
- };
-
- modal.style.display = "block";
-
- document.onkeydown = function(evt) {
- evt = evt || window.event;
- if (evt.keyCode == 27) {
- closeModal();
- }
- };
-
- function closeModal() {
- for (var i=0; i src links that don't have a file extension
-// If the image src doesn't have a file type:
-// 1. Create a dummy link
-// 2. Detect image type from the binary data & create new links
-// 3. Replace all the dummy links in tmpGlobalContent with the new links
-var tmpGlobalContent = null
-
+var allImgSrc = {};
var allImages = [];
-var extractedImages = [];
-var allowedTags = [
- 'address', 'article', 'aside', 'footer', 'header', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
- 'hgroup', 'nav', 'section', 'dd', 'div', 'dl', 'dt', 'figcaption', 'figure', 'hr', 'li',
- 'main', 'ol', 'p', 'pre', 'ul', 'a', 'abbr', 'b', 'bdi', 'bdo', 'br', 'cite', 'code', 'data',
- 'dfn', 'em', 'i', 'img', 'kbd', 'mark', 'q', 'rb', 'rp', 'rt', 'rtc', 'ruby', 's', 'samp', 'small', 'span',
- 'strong', 'sub', 'sup', 'time', 'u', 'var', 'wbr', 'del', 'ins', 'caption', 'col', 'colgroup',
- 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr',
- 'math', 'maction', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot',
- 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover', 'msgroup', 'mlongdiv', 'mscarries',
- 'mscarry', 'mstack', 'semantics'
- // TODO ?
- // ,'form', 'button'
-
- // TODO svg support ?
- // , 'svg', 'g', 'path', 'line', 'circle', 'text'
-];
-// const svgTags = ['svg', 'g', 'path', 'line', 'circle', 'text']
-var mathMLTags = [
- 'math', 'maction', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot',
- 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover', 'msgroup', 'mlongdiv', 'mscarries',
- 'mscarry', 'mstack', 'semantics'
-]
-var cssClassesToTmpIds = {};
-var tmpIdsToNewCss = {};
-var tmpIdsToNewCssSTRING = {};
-
-// src: https://idpf.github.io/a11y-guidelines/content/style/reference.html
-var supportedCss = [
- 'background-color',
- 'border',
- 'color',
- 'font',
- 'line-height',
- 'list-style',
- 'padding',
- 'text-align',
-];
+var maxNrOfElements = 10000;
//////
function getImageSrc(srcTxt) {
if (!srcTxt) {
return '';
}
- srcTxt = srcTxt.trim();
- if (srcTxt === '') {
- return '';
- }
-
- // TODO move
- srcTxt = srcTxt.replace(/&/g, '&')
-
- // TODO - convert with svg sources to jpeg OR add support for svg
-
- let fileExtension = getFileExtension(srcTxt);
- if (fileExtension === '') {
- fileExtension = "TODO-EXTRACT"
- }
- let newImgFileName = 'img-' + generateRandomNumber(true) + '.' + fileExtension;
-
- let isB64Img = isBase64Img(srcTxt);
- if (isB64Img) {
- extractedImages.push({
- filename: newImgFileName, // TODO name
- data: getBase64ImgData(srcTxt)
- });
- } else {
- allImages.push({
- originalUrl: getImgDownloadUrl(srcTxt),
- filename: newImgFileName, // TODO name
- });
- }
-
- return '../images/' + newImgFileName;
+ allImgSrc[srcTxt] = 'img-' + (Math.floor(Math.random()*1000000)) + '.' + getFileExtension(srcTxt);
+ return '../images/' + allImgSrc[srcTxt];
}
-// tested
-function extractMathMl($htmlObject) {
- $htmlObject.find('span[id^="MathJax-Element-"]').each(function (i, el) {
- $(el).replaceWith('' + el.getAttribute('data-mathml') + '');
- });
+function generateRandomTag() {
+ var text = "";
+ var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+ for(var i = 0; i < 5; i++)
+ text += possible.charAt(Math.floor(Math.random() * possible.length));
+
+ return text;
}
-// tested
-function extractCanvasToImg($htmlObject) {
- $htmlObject.find('canvas').each(function (index, elem) {
- try {
- let imgUrl = docEl.toDataURL('image/jpeg');
- $(elem).replaceWith('
');
- } catch (e) {
- console.log(e)
- }
- });
-}
-// tested
-function extractSvgToImg($htmlObject) {
- let serializer = new XMLSerializer();
- $htmlObject.find('svg').each(function (index, elem) {
- // add width & height because the result image was too big
- let bbox = elem.getBoundingClientRect()
- let newWidth = bbox.width
- let newHeight = bbox.height
- let svgXml = serializer.serializeToString(elem);
- let imgSrc = 'data:image/svg+xml;base64,' + window.btoa(svgXml);
- $(elem).replaceWith('
' + '');
- });
-}
-
-// replaces all iframes by divs with the same innerHTML content
-function extractIFrames() {
- let allIframes = document.getElementsByTagName('iframe')
- let changeIFrames = []
- let newDivs = []
- for (let iFrame of allIframes) {
- if (!iFrame.contentDocument || !iFrame.contentDocument.body) {
- continue
- }
- let bodyContent = iFrame.contentDocument.body.innerHTML
- let bbox = iFrame.getBoundingClientRect()
- let newDiv = document.createElement('div')
- newDiv.style.width = bbox.width
- newDiv.style.height = bbox.height
- newDiv.innerHTML = bodyContent
- changeIFrames.push(iFrame)
- newDivs.push(newDiv)
- }
- for (let i = 0; i < newDivs.length; i++) {
- let newDiv = newDivs[i]
- let iFrame = changeIFrames[i]
- let iframeParent = iFrame.parentNode
- iframeParent.replaceChild(newDiv, iFrame)
- }
-}
-
-function preProcess($htmlObject) {
- // TODO
- // $htmlObject.find('script, style, noscript, iframe').remove();
- // $('body').find('script, style, noscript, iframe').remove()
- // $('body').find('script, style, noscript, iframe').contents().remove()
- // $('body').find('iframe').remove()
- // $('body').find('*:empty').not('img').not('br').not('hr').remove();
- // formatPreCodeElements($('body'));
-
- extractMathMl($htmlObject);
- extractCanvasToImg($htmlObject);
- extractSvgToImg($htmlObject);
-}
-
-function parseHTML(rawContentString) {
- allImages = [];
- extractedImages = [];
- let results = '';
- let lastFragment = '';
- let lastTag = '';
+// function force3(dirty) {
+// var tagOpen = '@@@';// + generateRandomTag();
+// var tagClose = '###';// + generateRandomTag();
+// var removeElements = ['script', 'style', 'svg', 'canvas', 'noscript'];
+// var inlineElements = ['h1', 'h2', 'h3', 'sup', 'b', 'i', 'em', 'code', 'pre', 'p'];
+// var replaceElements = [['li', 'p'], ['tr', 'p']];
+//
+// // var bodyClone = document.getElementsByTagName('body')[0].cloneNode(true);
+//
+// var bodyClone = document.createElement('div');
+// bodyClone.innerHTML = dirty;
+//
+//
+// /////
+//
+// var imgs = bodyClone.getElementsByTagName('img');
+// for (var i = 0; i < imgs.length; i++) {
+// var newImg = document.createElement('span');
+// newImg.innerHTML = tagOpen + 'img src="' + getImageSrc(imgs[i].getAttribute('src')) + '"' + tagClose + tagOpen + '/img' + tagClose;
+// imgs[i].parentNode.replaceChild(newImg, imgs[i]);
+// }
+//
+// var links = bodyClone.getElementsByTagName('a');
+// for (i = 0; i < links.length; i++) {
+// var newLink = document.createElement('span');
+// newLink.innerHTML = tagOpen + 'a href="' + getHref(links[i].getAttribute('href')) + '"' + tagClose + links[i].innerHTML + tagOpen + '/a' + tagClose;
+// links[i].parentNode.replaceChild(newLink, links[i]);
+// }
+//
+// for (i = 0; i < inlineElements.length; i++) {
+// var tagName = inlineElements[i];
+// var miscElements = bodyClone.getElementsByTagName(tagName);
+// for (var j = 0; j < miscElements.length; j++) {
+// var elemToBeReplaced = miscElements[j];
+// var newElement = document.createElement('span');
+// newElement.innerHTML = tagOpen + tagName + tagClose + elemToBeReplaced.innerHTML + tagOpen + '/' + tagName + tagClose;
+// elemToBeReplaced.parentNode.replaceChild(newElement, elemToBeReplaced);
+// }
+// }
+//
+// for (i = 0; i < replaceElements.length; i++) {
+// var crtTagPair = replaceElements[i];
+// var searchForTag = crtTagPair[0];
+// var replaceWithTag = crtTagPair[1];
+// var miscElements = bodyClone.getElementsByTagName(searchForTag);
+// for (var j = 0; j < miscElements.length; j++) {
+// var elemToBeReplaced = miscElements[j];
+// var newElement = document.createElement('span');
+// newElement.innerHTML = tagOpen + replaceWithTag + tagClose + elemToBeReplaced.innerHTML + tagOpen + '/' + replaceWithTag + tagClose;
+// elemToBeReplaced.parentNode.replaceChild(newElement, elemToBeReplaced);
+// }
+// }
+//
+// var contentString = bodyClone.innerText;
+//
+// var tagOpenRegex = new RegExp(tagOpen, 'gi');
+// var tagCloseRegex = new RegExp(tagClose, 'gi');
+// contentString = contentString.replace(tagOpenRegex, '<');
+// contentString = contentString.replace(tagCloseRegex, '>');
+// contentString = contentString.replace(/&/gi, '&');
+// contentString = contentString.replace(/&/gi, '&');
+//
+// return contentString;
+//
+// }
+function force(contentString) {
try {
- HTMLParser(rawContentString, {
+ var tagOpen = '@@@' + generateRandomTag();
+ var tagClose = '###' + generateRandomTag();
+ var inlineElements = ['h1', 'h2', 'h3', 'sup', 'b', 'i', 'em', 'code', 'pre', 'p'];
+ var replaceElements = [['li', 'p'], ['tr', 'p']];
+
+ var $content = $(contentString);
+
+ $content.find('img').each(function (index, elem) {
+ $(elem).replaceWith('' + tagOpen + 'img src="' + getImageSrc($(elem).attr('src')) + '"' + tagClose + tagOpen + '/img' + tagClose + '');
+ });
+
+ $content.find('a').each(function (index, elem) {
+ $(elem).replaceWith('' + tagOpen + 'a href="' + getHref($(elem).attr('href')) + '"' + tagClose + $(elem).html() + tagOpen + '/a' + tagClose + '');
+ });
+
+ if ($('*').length < maxNrOfElements) {
+ replaceElements.forEach(function (replacePair) {
+ var searchFor = replacePair[0];
+ var tagName = replacePair[1];
+ var tmpElems = $content.find(searchFor);
+ while (tmpElems.length > 0) {
+ $tmpElem = $(tmpElems[0]);
+ $tmpElem.replaceWith('' + tagOpen + tagName + tagClose + $tmpElem.html() + tagOpen + '/' + tagName + tagClose + '');
+ tmpElems = $content.find(searchFor);
+ }
+ });
+
+ inlineElements.forEach(function (tagName) {
+ var tmpElems = $content.find(tagName);
+ while (tmpElems.length > 0) {
+ $tmpElem = $(tmpElems[0]);
+ $tmpElem.replaceWith('' + tagOpen + tagName + tagClose + $tmpElem.html() + tagOpen + '/' + tagName + tagClose + '');
+ tmpElems = $content.find(tagName);
+ }
+ });
+ }
+
+ contentString = $content.text();
+
+ var tagOpenRegex = new RegExp(tagOpen, 'gi');
+ var tagCloseRegex = new RegExp(tagClose, 'gi');
+ contentString = contentString.replace(tagOpenRegex, '<');
+ contentString = contentString.replace(tagCloseRegex, '>');
+ contentString = contentString.replace(/&/gi, '&');
+ contentString = contentString.replace(/&/gi, '&');
+
+ return contentString;
+ } catch (e) {
+ console.log('Error:', e);
+ }
+}
+
+// https://github.com/blowsie/Pure-JavaScript-HTML5-Parser
+function sanitize(rawContentString) {
+ allImgSrc = {};
+ var srcTxt = '';
+ var dirty = null;
+ try {
+ // dirty = getHtmlAsString(rawContent);
+ wdirty = $.parseHTML(rawContentString);
+ $wdirty = $(wdirty);
+ $wdirty.find('script, style, svg, canvas, noscript').remove();
+ $wdirty.find('*:empty').not('img').remove();
+
+ dirty = '' + $wdirty.html() + '
';
+
+ ////////////////
+ if ($('*').length > maxNrOfElements) {
+ return force(dirty);
+ }
+
+ var results = '';
+ var lastFragment = '';
+ var lastTag = '';
+ var inList = false;
+ var allowedTags = ['div', 'p', 'code', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'span', 'blockquote',
+ 'img', 'a', 'ol', 'ul', 'li', 'b', 'i', 'sup', 'strong', 'strike',
+ 'table', 'tr', 'td', 'th', 'thead', 'tbody', 'pre', 'em'
+ ];
+ var allowedTextTags = ['h4', 'h5', 'h6', 'span'];
+
+ HTMLParser(dirty, {
start: function(tag, attrs, unary) {
lastTag = tag;
if (allowedTags.indexOf(tag) < 0) {
return;
}
+ if (tag === 'ol' || tag === 'ul') {
+ inList = true;
+ }
+ if (tag === 'li' && !inList) {
+ tag = 'p';
+ }
+
+ var tattrs = null;
if (tag === 'img') {
- let tmpAttrsTxt = '';
- let tmpSrc = ''
- for (let i = 0; i < attrs.length; i++) {
- if (attrs[i].name === 'src') {
- tmpSrc = getImageSrc(attrs[i].value)
- tmpAttrsTxt += ' src="' + tmpSrc + '"';
- } else if (attrs[i].name === 'data-class') {
- tmpAttrsTxt += ' class="' + attrs[i].value + '"';
- } else if (attrs[i].name === 'width') {
- // used when converting svg to img - the result image was too big
- tmpAttrsTxt += ' width="' + attrs[i].value + '"';
- } else if (attrs[i].name === 'height') {
- // used when converting svg to img - the result image was too big
- tmpAttrsTxt += ' height="' + attrs[i].value + '"';
- }
- }
- if (tmpSrc === '') {
- // ignore imgs without source
- lastFragment = ''
- } else {
- lastFragment = tmpAttrsTxt.length === 0 ? '
' : '
';
- }
+ tattrs = attrs.filter(function(attr) {
+ return attr.name === 'src';
+ }).map(function(attr) {
+ return getImageSrc(attr.escaped);
+ });
+ lastFragment = tattrs.length === 0 ? '
' : '
';
} else if (tag === 'a') {
- let tmpAttrsTxt = '';
- for (let i = 0; i < attrs.length; i++) {
- if (attrs[i].name === 'href') {
- tmpAttrsTxt += ' href="' + getHref(attrs[i].value) + '"';
- } else if (attrs[i].name === 'data-class') {
- tmpAttrsTxt += ' class="' + attrs[i].value + '"';
- }
- }
- lastFragment = tmpAttrsTxt.length === 0 ? '' : '';
- } else if (tag === 'br' || tag === 'hr') {
- let tmpAttrsTxt = '';
- for (let i = 0; i < attrs.length; i++) {
- if (attrs[i].name === 'data-class') {
- tmpAttrsTxt += ' class="' + attrs[i].value + '"';
- }
- }
- lastFragment = '<' + tag + tmpAttrsTxt + '>' + tag + '>';
- } else if (tag === 'math') {
- let tmpAttrsTxt = '';
- tmpAttrsTxt += ' xmlns="http://www.w3.org/1998/Math/MathML"';
- for (let i = 0; i < attrs.length; i++) {
- if (attrs[i].name === 'alttext') {
- tmpAttrsTxt += ' alttext="' + attrs[i].value + '"';
- }
- }
- lastFragment = '<' + tag + tmpAttrsTxt + '>';
+ tattrs = attrs.filter(function(attr) {
+ return attr.name === 'href';
+ }).map(function(attr) {
+ return getHref(attr.escaped);
+ });
+ lastFragment = tattrs.length === 0 ? '' : '';
} else {
- let tmpAttrsTxt = '';
- for (let i = 0; i < attrs.length; i++) {
- if (attrs[i].name === 'data-class') {
- tmpAttrsTxt += ' class="' + attrs[i].value + '"';
- }
- }
- lastFragment = '<' + tag + tmpAttrsTxt + '>';
+ lastFragment = '<' + tag + '>';
}
results += lastFragment;
lastFragment = '';
},
end: function(tag) {
- if (allowedTags.indexOf(tag) < 0 || tag === 'img' || tag === 'br' || tag === 'hr') {
+ if (allowedTags.indexOf(tag) < 0 || tag === 'img') {
return;
}
- results += "" + tag + ">";
+ if (tag === 'ol' || tag === 'ul') {
+ inList = false;
+ }
+ if (tag === 'li' && !inList) {
+ tag = 'p';
+ }
+
+ results += "" + tag + ">\n";
},
chars: function(text) {
if (lastTag !== '' && allowedTags.indexOf(lastTag) < 0) {
@@ -252,42 +232,49 @@ function parseHTML(rawContentString) {
}
});
- // TODO - (re)move
- results = results.replace(/ /gi, ' ');
+ // results = results.replace(/&[a-z]+;/gim, '');
+ results = results.replace(/&/gi, '&');
+ results = results.replace(/&/gi, '&');
return results;
} catch (e) {
console.log('Error:', e);
- return 'Error: ' + e //+" " + force($(rawContentString))
+ return force(dirty);
}
}
function getContent(htmlContent) {
try {
- // TODO - move; called multiple times on selection
- preProcess($('body'))
- let tmp = document.createElement('div');
+ var tmp = document.createElement('div');
tmp.appendChild(htmlContent.cloneNode(true));
- let tmpHtml = '' + tmp.innerHTML + '
';
- return parseHTML(tmpHtml);
+ var dirty = '' + tmp.innerHTML + '
';
+ return sanitize(dirty);
} catch (e) {
console.log('Error:', e);
- return htmlContent;
+ return '';
}
}
/////
+function getPageUrl(url) {
+ return url.toLowerCase().replace(/\s+/g,'_').replace(/[^a-z0-9_]/g,'') + Math.floor(Math.random() * 10000) + '.xhtml';
+}
+
+function getPageTitle(title) { //TODO
+ return title;
+}
+
function getSelectedNodes() {
// if (document.selection) {
// return document.selection.createRange().parentElement();
// return document.selection.createRange();
// }
- let selection = window.getSelection();
- let docfrag = [];
- for (let i = 0; i < selection.rangeCount; i++) {
+ var selection = window.getSelection();
+ var docfrag = [];
+ for (var i = 0; i < selection.rangeCount; i++) {
docfrag.push(selection.getRangeAt(i).cloneContents());
}
return docfrag;
@@ -295,174 +282,67 @@ function getSelectedNodes() {
/////
-function extractCss(includeStyle, appliedStyles) {
- if (includeStyle) {
- $('body').find('*').each((i, pre) => {
- let $pre = $(pre);
-
- if (allowedTags.indexOf(pre.tagName.toLowerCase()) < 0) return;
- if (mathMLTags.indexOf(pre.tagName.toLowerCase()) > -1) return;
-
- if (!$pre.is(':visible')) {
- $pre.replaceWith('');
- } else {
- if (pre.tagName.toLowerCase() === 'svg') return;
-
- let classNames = pre.getAttribute('class');
- if (!classNames) {
- classNames = pre.getAttribute('id');
- if (!classNames) {
- classNames = pre.tagName + '-' + generateRandomNumber();
- }
- }
- let tmpName = cssClassesToTmpIds[classNames];
- let tmpNewCss = tmpIdsToNewCss[tmpName];
- if (!tmpName) {
- // TODO - collision between class names when multiple pages
- tmpName = generateRandomTag(2) + i
- cssClassesToTmpIds[classNames] = tmpName;
- }
- if (!tmpNewCss) {
- tmpNewCss = {};
-
- for (let cssTagName of supportedCss) {
- let cssValue = $pre.css(cssTagName);
- if (cssValue && cssValue.length > 0) {
- tmpNewCss[cssTagName] = cssValue;
- }
- }
-
- // Reuse CSS - if the same css code was generated for another element, reuse it's class name
-
- let tcss = JSON.stringify(tmpNewCss)
- let found = false
-
- if (Object.keys(tmpIdsToNewCssSTRING).length === 0) {
- tmpIdsToNewCssSTRING[tmpName] = tcss;
- tmpIdsToNewCss[tmpName] = tmpNewCss;
- } else {
- for (const key in tmpIdsToNewCssSTRING) {
- if (tmpIdsToNewCssSTRING[key] === tcss) {
- tmpName = key
- found = true
- break
- }
- }
- if (!found) {
- tmpIdsToNewCssSTRING[tmpName] = tcss;
- tmpIdsToNewCss[tmpName] = tmpNewCss;
- }
- }
- }
- pre.setAttribute('data-class', tmpName);
- }
- });
- return jsonToCss(tmpIdsToNewCss);
- } else {
- // remove hidden elements when style is not included
- $('body').find('*').each((i, pre) => {
- let $pre = $(pre)
- if (!$pre.is(':visible')) {
- $pre.replaceWith('')
- }
- })
- let mergedCss = '';
- if (appliedStyles && appliedStyles.length > 0) {
- for (let i = 0; i < appliedStyles.length; i++) {
- mergedCss += appliedStyles[i].style;
- }
- return mergedCss;
- }
- }
- return null
-}
-
-/////
-
-function deferredAddZip(url, filename) {
- let deferred = $.Deferred();
- JSZipUtils.getBinaryContent(url, function(err, data) {
+function deferredAddZip(url, filename, zip) {
+ var deferred = $.Deferred();
+ JSZipUtils.getBinaryContent(url, function(err, data) {
if (err) {
// deferred.reject(err); TODO
- console.log('Error:', err);
deferred.resolve();
} else {
- // TODO - move to utils.js
- if (filename.endsWith("TODO-EXTRACT")) {
- let oldFilename = filename
- let arr = (new Uint8Array(data)).subarray(0, 4);
- let header = "";
- for(let i = 0; i < arr.length; i++) {
- header += arr[i].toString(16);
- }
- if (header.startsWith("89504e47")) {
- filename = filename.replace("TODO-EXTRACT", "png")
- } else if (header.startsWith("47494638")) {
- filename = filename.replace("TODO-EXTRACT", "gif")
- } else if (header.startsWith("ffd8ff")) {
- filename = filename.replace("TODO-EXTRACT", "jpg")
- } else {
- // ERROR
- console.log("Error! Unable to extract the image type!");
- deferred.resolve();
- }
- tmpGlobalContent = tmpGlobalContent.replace(oldFilename, filename)
- }
-
- extractedImages.push({
+ var tmpImg = {
filename: filename,
- // TODO - must be JSON serializable
data: base64ArrayBuffer(data)
- });
-
+ };
+ allImages.push(tmpImg);
deferred.resolve();
}
});
return deferred;
}
-chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
- let imgsPromises = [];
- let result = {};
- let pageSrc = '';
- let tmpContent = '';
- let styleFile = null;
-
- extractIFrames()
+chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
+ console.log('Extract Html...');
+ var imgsPromises = [];
+ allImgSrc = {};
+ allImages = [];
+ var result = {};
+ var pageSrc = '';
+ var tmpContent = '';
if (request.type === 'extract-page') {
- styleFile = extractCss(request.includeStyle, request.appliedStyles)
pageSrc = document.getElementsByTagName('body')[0];
tmpContent = getContent(pageSrc);
} else if (request.type === 'extract-selection') {
- styleFile = extractCss(request.includeStyle, request.appliedStyles)
pageSrc = getSelectedNodes();
- pageSrc.forEach((page) => {
+ pageSrc.forEach(function (page) {
tmpContent += getContent(page);
});
}
- tmpGlobalContent = tmpContent
+ if (tmpContent.trim() === '') {
+ return;
+ }
- allImages.forEach((tmpImg) => {
- imgsPromises.push(deferredAddZip(tmpImg.originalUrl, tmpImg.filename));
+ Object.keys(allImgSrc).forEach(function(imgSrc, index) {
+ try {
+ var tmpDeffered = deferredAddZip(getImgDownloadUrl(imgSrc), allImgSrc[imgSrc]);
+ imgsPromises.push(tmpDeffered);
+ } catch (e) {
+ console.log('Error:', e);
+ }
});
- $.when.apply($, imgsPromises).done(() => {
- let tmpTitle = getPageTitle(document.title);
+ $.when.apply($, imgsPromises).done(function() {
result = {
- url: getPageUrl(tmpTitle),
- title: tmpTitle,
+ url: getPageUrl(document.title),
+ title: getPageTitle(document.title),
baseUrl: getCurrentUrl(),
- styleFileContent: styleFile,
- styleFileName: 'style' + generateRandomNumber() + '.css',
- images: extractedImages,
- content: tmpGlobalContent
+ images: allImages,
+ content: tmpContent
};
sendResponse(result);
- }).fail((e) => {
+ }).fail(function(e) {
console.log('Error:', e);
- sendResponse(null)
});
return true;
diff --git a/web-extension/filesaver.js b/web-extension/filesaver.js
new file mode 100644
index 0000000..239db12
--- /dev/null
+++ b/web-extension/filesaver.js
@@ -0,0 +1,188 @@
+/* FileSaver.js
+ * A saveAs() FileSaver implementation.
+ * 1.3.2
+ * 2016-06-16 18:25:19
+ *
+ * By Eli Grey, http://eligrey.com
+ * License: MIT
+ * See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
+ */
+
+/*global self */
+/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
+
+/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
+
+var saveAs = saveAs || (function(view) {
+ "use strict";
+ // IE <10 is explicitly unsupported
+ if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
+ return;
+ }
+ var
+ doc = view.document
+ // only get URL when necessary in case Blob.js hasn't overridden it yet
+ , get_URL = function() {
+ return view.URL || view.webkitURL || view;
+ }
+ , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
+ , can_use_save_link = "download" in save_link
+ , click = function(node) {
+ var event = new MouseEvent("click");
+ node.dispatchEvent(event);
+ }
+ , is_safari = /constructor/i.test(view.HTMLElement)
+ , is_chrome_ios =/CriOS\/[\d]+/.test(navigator.userAgent)
+ , throw_outside = function(ex) {
+ (view.setImmediate || view.setTimeout)(function() {
+ throw ex;
+ }, 0);
+ }
+ , force_saveable_type = "application/octet-stream"
+ // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
+ , arbitrary_revoke_timeout = 1000 * 40 // in ms
+ , revoke = function(file) {
+ var revoker = function() {
+ if (typeof file === "string") { // file is an object URL
+ get_URL().revokeObjectURL(file);
+ } else { // file is a File
+ file.remove();
+ }
+ };
+ setTimeout(revoker, arbitrary_revoke_timeout);
+ }
+ , dispatch = function(filesaver, event_types, event) {
+ event_types = [].concat(event_types);
+ var i = event_types.length;
+ while (i--) {
+ var listener = filesaver["on" + event_types[i]];
+ if (typeof listener === "function") {
+ try {
+ listener.call(filesaver, event || filesaver);
+ } catch (ex) {
+ throw_outside(ex);
+ }
+ }
+ }
+ }
+ , auto_bom = function(blob) {
+ // prepend BOM for UTF-8 XML and text/* types (including HTML)
+ // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
+ if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
+ return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type});
+ }
+ return blob;
+ }
+ , FileSaver = function(blob, name, no_auto_bom) {
+ if (!no_auto_bom) {
+ blob = auto_bom(blob);
+ }
+ // First try a.download, then web filesystem, then object URLs
+ var
+ filesaver = this
+ , type = blob.type
+ , force = type === force_saveable_type
+ , object_url
+ , dispatch_all = function() {
+ dispatch(filesaver, "writestart progress write writeend".split(" "));
+ }
+ // on any filesys errors revert to saving with object URLs
+ , fs_error = function() {
+ if ((is_chrome_ios || (force && is_safari)) && view.FileReader) {
+ // Safari doesn't allow downloading of blob urls
+ var reader = new FileReader();
+ reader.onloadend = function() {
+ var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;');
+ var popup = view.open(url, '_blank');
+ if(!popup) view.location.href = url;
+ url=undefined; // release reference before dispatching
+ filesaver.readyState = filesaver.DONE;
+ dispatch_all();
+ };
+ reader.readAsDataURL(blob);
+ filesaver.readyState = filesaver.INIT;
+ return;
+ }
+ // don't create more object URLs than needed
+ if (!object_url) {
+ object_url = get_URL().createObjectURL(blob);
+ }
+ if (force) {
+ view.location.href = object_url;
+ } else {
+ var opened = view.open(object_url, "_blank");
+ if (!opened) {
+ // Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
+ view.location.href = object_url;
+ }
+ }
+ filesaver.readyState = filesaver.DONE;
+ dispatch_all();
+ revoke(object_url);
+ }
+ ;
+ filesaver.readyState = filesaver.INIT;
+
+ if (can_use_save_link) {
+ object_url = get_URL().createObjectURL(blob);
+ setTimeout(function() {
+ save_link.href = object_url;
+ save_link.download = name;
+ click(save_link);
+ dispatch_all();
+ revoke(object_url);
+ filesaver.readyState = filesaver.DONE;
+ });
+ return;
+ }
+
+ fs_error();
+ }
+ , FS_proto = FileSaver.prototype
+ , saveAs = function(blob, name, no_auto_bom) {
+ return new FileSaver(blob, name || blob.name || "download", no_auto_bom);
+ }
+ ;
+ // IE 10+ (native saveAs)
+ if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
+ return function(blob, name, no_auto_bom) {
+ name = name || blob.name || "download";
+
+ if (!no_auto_bom) {
+ blob = auto_bom(blob);
+ }
+ return navigator.msSaveOrOpenBlob(blob, name);
+ };
+ }
+
+ FS_proto.abort = function(){};
+ FS_proto.readyState = FS_proto.INIT = 0;
+ FS_proto.WRITING = 1;
+ FS_proto.DONE = 2;
+
+ FS_proto.error =
+ FS_proto.onwritestart =
+ FS_proto.onprogress =
+ FS_proto.onwrite =
+ FS_proto.onabort =
+ FS_proto.onerror =
+ FS_proto.onwriteend =
+ null;
+
+ return saveAs;
+}(
+ typeof self !== "undefined" && self
+ || typeof window !== "undefined" && window
+ || this.content
+));
+// `self` is undefined in Firefox for Android content script context
+// while `this` is nsIContentFrameMessageManager
+// with an attribute `content` that corresponds to the window
+
+if (typeof module !== "undefined" && module.exports) {
+ module.exports.saveAs = saveAs;
+} else if ((typeof define !== "undefined" && define !== null) && (define.amd !== null)) {
+ define([], function() {
+ return saveAs;
+ });
+}
diff --git a/web-extension/icons/book128.png b/web-extension/icons/book128.png
index 14ae99c..3b5d8fd 100644
Binary files a/web-extension/icons/book128.png and b/web-extension/icons/book128.png differ
diff --git a/web-extension/icons/book32.png b/web-extension/icons/book32.png
index 84ee1f9..81d30eb 100644
Binary files a/web-extension/icons/book32.png and b/web-extension/icons/book32.png differ
diff --git a/web-extension/icons/book48.png b/web-extension/icons/book48.png
index d06befb..ada2f39 100644
Binary files a/web-extension/icons/book48.png and b/web-extension/icons/book48.png differ
diff --git a/web-extension/libs/jquery-sortable.js b/web-extension/jquery-sortable.js
similarity index 100%
rename from web-extension/libs/jquery-sortable.js
rename to web-extension/jquery-sortable.js
diff --git a/web-extension/libs/jquery.js b/web-extension/jquery.js
similarity index 100%
rename from web-extension/libs/jquery.js
rename to web-extension/jquery.js
diff --git a/web-extension/libs/jszip-utils.js b/web-extension/jszip-utils.js
similarity index 100%
rename from web-extension/libs/jszip-utils.js
rename to web-extension/jszip-utils.js
diff --git a/web-extension/libs/jszip.js b/web-extension/jszip.js
similarity index 100%
rename from web-extension/libs/jszip.js
rename to web-extension/jszip.js
diff --git a/web-extension/libs/cssjson.js b/web-extension/libs/cssjson.js
deleted file mode 100644
index 01b6332..0000000
--- a/web-extension/libs/cssjson.js
+++ /dev/null
@@ -1,297 +0,0 @@
-/**
- * CSS-JSON Converter for JavaScript
- * Converts CSS to JSON and back.
- * Version 2.1
- *
- * Released under the MIT license.
- *
- * Copyright (c) 2013 Aram Kocharyan, http://aramk.com/
-
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
- documentation files (the "Software"), to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
- to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all copies or substantial portions
- of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
- THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- */
-
-var CSSJSON = new function () {
-
- var base = this;
-
- base.init = function () {
- // String functions
- String.prototype.trim = function () {
- return this.replace(/^\s+|\s+$/g, '');
- };
-
- String.prototype.repeat = function (n) {
- return new Array(1 + n).join(this);
- };
- };
- base.init();
-
- var selX = /([^\s\;\{\}][^\;\{\}]*)\{/g;
- var endX = /\}/g;
- var lineX = /([^\;\{\}]*)\;/g;
- var commentX = /\/\*[\s\S]*?\*\//g;
- var lineAttrX = /([^\:]+):([^\;]*);/;
-
- // This is used, a concatenation of all above. We use alternation to
- // capture.
- var altX = /(\/\*[\s\S]*?\*\/)|([^\s\;\{\}][^\;\{\}]*(?=\{))|(\})|([^\;\{\}]+\;(?!\s*\*\/))/gmi;
-
- // Capture groups
- var capComment = 1;
- var capSelector = 2;
- var capEnd = 3;
- var capAttr = 4;
-
- var isEmpty = function (x) {
- return typeof x == 'undefined' || x.length == 0 || x == null;
- };
-
- var isCssJson = function (node) {
- return !isEmpty(node) ? (node.attributes && node.children) : false;
- }
-
- /**
- * Input is css string and current pos, returns JSON object
- *
- * @param cssString
- * The CSS string.
- * @param args
- * An optional argument object. ordered: Whether order of
- * comments and other nodes should be kept in the output. This
- * will return an object where all the keys are numbers and the
- * values are objects containing "name" and "value" keys for each
- * node. comments: Whether to capture comments. split: Whether to
- * split each comma separated list of selectors.
- */
- base.toJSON = function (cssString, args) {
- var node = {
- children: {},
- attributes: {}
- };
- var match = null;
- var count = 0;
-
- if (typeof args == 'undefined') {
- var args = {
- ordered: false,
- comments: false,
- stripComments: false,
- split: false
- };
- }
- if (args.stripComments) {
- args.comments = false;
- cssString = cssString.replace(commentX, '');
- }
-
- while ((match = altX.exec(cssString)) != null) {
- if (!isEmpty(match[capComment]) && args.comments) {
- // Comment
- var add = match[capComment].trim();
- node[count++] = add;
- } else if (!isEmpty(match[capSelector])) {
- // New node, we recurse
- var name = match[capSelector].trim();
- // This will return when we encounter a closing brace
- var newNode = base.toJSON(cssString, args);
- if (args.ordered) {
- var obj = {};
- obj['name'] = name;
- obj['value'] = newNode;
- // Since we must use key as index to keep order and not
- // name, this will differentiate between a Rule Node and an
- // Attribute, since both contain a name and value pair.
- obj['type'] = 'rule';
- node[count++] = obj;
- } else {
- if (args.split) {
- var bits = name.split(',');
- } else {
- var bits = [name];
- }
- for (i in bits) {
- var sel = bits[i].trim();
- if (sel in node.children) {
- for (var att in newNode.attributes) {
- node.children[sel].attributes[att] = newNode.attributes[att];
- }
- } else {
- node.children[sel] = newNode;
- }
- }
- }
- } else if (!isEmpty(match[capEnd])) {
- // Node has finished
- return node;
- } else if (!isEmpty(match[capAttr])) {
- var line = match[capAttr].trim();
- var attr = lineAttrX.exec(line);
- if (attr) {
- // Attribute
- var name = attr[1].trim();
- var value = attr[2].trim();
- if (args.ordered) {
- var obj = {};
- obj['name'] = name;
- obj['value'] = value;
- obj['type'] = 'attr';
- node[count++] = obj;
- } else {
- if (name in node.attributes) {
- var currVal = node.attributes[name];
- if (!(currVal instanceof Array)) {
- node.attributes[name] = [currVal];
- }
- node.attributes[name].push(value);
- } else {
- node.attributes[name] = value;
- }
- }
- } else {
- // Semicolon terminated line
- node[count++] = line;
- }
- }
- }
-
- return node;
- };
-
- /**
- * @param node
- * A JSON node.
- * @param depth
- * The depth of the current node; used for indentation and
- * optional.
- * @param breaks
- * Whether to add line breaks in the output.
- */
- base.toCSS = function (node, depth, breaks) {
- var cssString = '';
- if (typeof depth == 'undefined') {
- depth = 0;
- }
- if (typeof breaks == 'undefined') {
- breaks = false;
- }
- if (node.attributes) {
- for (i in node.attributes) {
- var att = node.attributes[i];
- if (att instanceof Array) {
- for (var j = 0; j < att.length; j++) {
- cssString += strAttr(i, att[j], depth);
- }
- } else {
- cssString += strAttr(i, att, depth);
- }
- }
- }
- if (node.children) {
- var first = true;
- for (i in node.children) {
- if (breaks && !first) {
- cssString += '\n';
- } else {
- first = false;
- }
- cssString += strNode(i, node.children[i], depth);
- }
- }
- return cssString;
- };
-
- /**
- * @param data
- * You can pass css string or the CSSJS.toJSON return value.
- * @param id (Optional)
- * To identify and easy removable of the style element
- * @param replace (Optional. defaults to TRUE)
- * Whether to remove or simply do nothing
- * @return HTMLLinkElement
- */
- base.toHEAD = function (data, id, replace) {
- var head = document.getElementsByTagName('head')[0];
- var xnode = document.getElementById(id);
- var _xnodeTest = (xnode !== null && xnode instanceof HTMLStyleElement);
-
- if (isEmpty(data) || !(head instanceof HTMLHeadElement)) return;
- if (_xnodeTest) {
- if (replace === true || isEmpty(replace)) {
- xnode.removeAttribute('id');
- } else return;
- }
- if (isCssJson(data)) {
- data = base.toCSS(data);
- }
-
- var node = document.createElement('style');
- node.type = 'text/css';
-
- if (!isEmpty(id)) {
- node.id = id;
- } else {
- node.id = 'cssjson_' + timestamp();
- }
- if (node.styleSheet) {
- node.styleSheet.cssText = data;
- } else {
- node.appendChild(document.createTextNode(data));
- }
-
- head.appendChild(node);
-
- if (isValidStyleNode(node)) {
- if (_xnodeTest) {
- xnode.parentNode.removeChild(xnode);
- }
- } else {
- node.parentNode.removeChild(node);
- if (_xnodeTest) {
- xnode.setAttribute('id', id);
- node = xnode;
- } else return;
- }
-
- return node;
- };
-
- // Alias
-
- if (typeof window != 'undefined') {
- window.createCSS = base.toHEAD;
- }
-
- // Helpers
-
- var isValidStyleNode = function (node) {
- return (node instanceof HTMLStyleElement) && node.sheet.cssRules.length > 0;
- }
-
- var timestamp = function () {
- return Date.now() || +new Date();
- };
-
- var strAttr = function (name, value, depth) {
- return '\t'.repeat(depth) + name + ': ' + value + ';\n';
- };
-
- var strNode = function (name, value, depth) {
- var cssString = '\t'.repeat(depth) + name + ' {\n';
- cssString += base.toCSS(value, depth + 1);
- cssString += '\t'.repeat(depth) + '}\n';
- return cssString;
- };
-
-};
diff --git a/web-extension/libs/filesaver.js b/web-extension/libs/filesaver.js
deleted file mode 100644
index 5d204ae..0000000
--- a/web-extension/libs/filesaver.js
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
-* FileSaver.js
-* A saveAs() FileSaver implementation.
-*
-* By Eli Grey, http://eligrey.com
-*
-* License : https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md (MIT)
-* source : http://purl.eligrey.com/github/FileSaver.js
-*/
-
-// The one and only way of getting global scope in all environments
-// https://stackoverflow.com/q/3277182/1008999
-var _global = typeof window === 'object' && window.window === window
- ? window : typeof self === 'object' && self.self === self
- ? self : typeof global === 'object' && global.global === global
- ? global
- : this
-
-function bom (blob, opts) {
- if (typeof opts === 'undefined') opts = { autoBom: false }
- else if (typeof opts !== 'object') {
- console.warn('Deprecated: Expected third argument to be a object')
- opts = { autoBom: !opts }
- }
-
- // prepend BOM for UTF-8 XML and text/* types (including HTML)
- // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
- if (opts.autoBom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
- return new Blob([String.fromCharCode(0xFEFF), blob], { type: blob.type })
- }
- return blob
-}
-
-function download (url, name, opts) {
- var xhr = new XMLHttpRequest()
- xhr.open('GET', url)
- xhr.responseType = 'blob'
- xhr.onload = function () {
- saveAs(xhr.response, name, opts)
- }
- xhr.onerror = function () {
- console.error('could not download file')
- }
- xhr.send()
-}
-
-function corsEnabled (url) {
- var xhr = new XMLHttpRequest()
- // use sync to avoid popup blocker
- xhr.open('HEAD', url, false)
- try {
- xhr.send()
- } catch (e) {}
- return xhr.status >= 200 && xhr.status <= 299
-}
-
-// `a.click()` doesn't work for all browsers (#465)
-function click (node) {
- try {
- node.dispatchEvent(new MouseEvent('click'))
- } catch (e) {
- var evt = document.createEvent('MouseEvents')
- evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80,
- 20, false, false, false, false, 0, null)
- node.dispatchEvent(evt)
- }
-}
-
-// Detect WebView inside a native macOS app by ruling out all browsers
-// We just need to check for 'Safari' because all other browsers (besides Firefox) include that too
-// https://www.whatismybrowser.com/guides/the-latest-user-agent/macos
-var isMacOSWebView = _global.navigator && /Macintosh/.test(navigator.userAgent) && /AppleWebKit/.test(navigator.userAgent) && !/Safari/.test(navigator.userAgent)
-
-var saveAs = _global.saveAs || (
- // probably in some web worker
- (typeof window !== 'object' || window !== _global)
- ? function saveAs () { /* noop */ }
-
- // Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView
- : ('download' in HTMLAnchorElement.prototype && !isMacOSWebView)
- ? function saveAs (blob, name, opts) {
- var URL = _global.URL || _global.webkitURL
- var a = document.createElement('a')
- name = name || blob.name || 'download'
-
- a.download = name
- a.rel = 'noopener' // tabnabbing
-
- // TODO: detect chrome extensions & packaged apps
- // a.target = '_blank'
-
- if (typeof blob === 'string') {
- // Support regular links
- a.href = blob
- if (a.origin !== location.origin) {
- corsEnabled(a.href)
- ? download(blob, name, opts)
- : click(a, a.target = '_blank')
- } else {
- click(a)
- }
- } else {
- // Support blobs
- a.href = URL.createObjectURL(blob)
- setTimeout(function () { URL.revokeObjectURL(a.href) }, 4E4) // 40s
- setTimeout(function () { click(a) }, 0)
- }
- }
-
- // Use msSaveOrOpenBlob as a second approach
- : 'msSaveOrOpenBlob' in navigator
- ? function saveAs (blob, name, opts) {
- name = name || blob.name || 'download'
-
- if (typeof blob === 'string') {
- if (corsEnabled(blob)) {
- download(blob, name, opts)
- } else {
- var a = document.createElement('a')
- a.href = blob
- a.target = '_blank'
- setTimeout(function () { click(a) })
- }
- } else {
- navigator.msSaveOrOpenBlob(bom(blob, opts), name)
- }
- }
-
- // Fallback to using FileReader and a popup
- : function saveAs (blob, name, opts, popup) {
- // Open a popup immediately do go around popup blocker
- // Mostly only available on user interaction and the fileReader is async so...
- popup = popup || open('', '_blank')
- if (popup) {
- popup.document.title =
- popup.document.body.innerText = 'downloading...'
- }
-
- if (typeof blob === 'string') return download(blob, name, opts)
-
- var force = blob.type === 'application/octet-stream'
- var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari
- var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent)
-
- if ((isChromeIOS || (force && isSafari) || isMacOSWebView) && typeof FileReader !== 'undefined') {
- // Safari doesn't allow downloading of blob URLs
- var reader = new FileReader()
- reader.onloadend = function () {
- var url = reader.result
- url = isChromeIOS ? url : url.replace(/^data:[^;]*;/, 'data:attachment/file;')
- if (popup) popup.location.href = url
- else location = url
- popup = null // reverse-tabnabbing #460
- }
- reader.readAsDataURL(blob)
- } else {
- var URL = _global.URL || _global.webkitURL
- var url = URL.createObjectURL(blob)
- if (popup) popup.location = url
- else location.href = url
- popup = null // reverse-tabnabbing #460
- setTimeout(function () { URL.revokeObjectURL(url) }, 4E4) // 40s
- }
- }
-)
-
-_global.saveAs = saveAs.saveAs = saveAs
-
-if (typeof module !== 'undefined') {
- module.exports = saveAs;
-}
diff --git a/web-extension/manifest.json b/web-extension/manifest.json
index 0b65008..d0a90e3 100644
--- a/web-extension/manifest.json
+++ b/web-extension/manifest.json
@@ -1,60 +1,39 @@
{
+
"manifest_version": 2,
- "name": "__MSG_extName__",
- "version": "1.4.2",
- "default_locale": "en",
- "author": "Alex Adam",
- "homepage_url": "https://github.com/alexadam/save-as-ebook",
+ "name": "Save As eBook",
+ "version": "1.0",
+
"description": "Save a web page or selection as eBook (.epub)",
+
"icons": {
"48": "icons/book48.png"
},
+
"content_scripts": [{
"matches": [""],
- "js": ["./libs/jquery.js", "./libs/jszip.js", "./libs/jszip-utils.js",
- "./libs/pure-parser.js", "./libs/cssjson.js", "./libs/filesaver.js",
- "saveEbook.js", "extractHtml.js", "utils.js"]
+ "js": ["jquery.js", "jszip.js", "jszip-utils.js", "utils.js", "pure-parser.js", "extractHtml.js"]
}],
+
"background": {
"scripts": ["background.js"]
},
+
"browser_action": {
"default_icon": "icons/book32.png",
"default_title": "Save as eBook",
"default_popup": "menu.html"
},
+
"permissions": [
"contextMenus",
"activeTab",
"storage",
- "unlimitedStorage",
+ "tabs",
"notifications",
+ "http://*/*",
+ "https://*/*",
""
- ],
- "commands": {
- "save-page": {
- "suggested_key": {
- "default": "Alt+Shift+1"
- },
- "description": "Save current page as eBook"
- },
- "save-selection": {
- "suggested_key": {
- "default": "Alt+Shift+2"
- },
- "description": "Save current selection as eBook"
- },
- "add-page": {
- "suggested_key": {
- "default": "Alt+Shift+3"
- },
- "description": "Add current page as chapter"
- },
- "add-selection": {
- "suggested_key": {
- "default": "Alt+Shift+4"
- },
- "description": "Add current selection as chapter"
- }
- }
+ ]
+
}
diff --git a/web-extension/menu.html b/web-extension/menu.html
index 0906d7b..7973fba 100644
--- a/web-extension/menu.html
+++ b/web-extension/menu.html
@@ -2,162 +2,57 @@
-
-
+ button:hover {
+ background-color: #007fff;
+ color: white;
+ }
+
-