Commit 76538b3e authored by Duhoux Pierre-Louis's avatar Duhoux Pierre-Louis

Merge branch 'develop' into 'master'

Version 2.1.0

See merge request !39
parents 3dbe898c e44cf197
Pipeline #40465292 (#124) passed with stages
in 5 minutes and 21 seconds
......@@ -5,7 +5,11 @@ stages:
- package
before_script:
- export LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null)
- export PREVIOUS_TAGS=$(git tag --list --format '%(objectname)' --merged origin/master --no-contains HEAD)
- export PREVIOUS_TAGS+=" $(git tag --list --format '%(objectname)' --points-at HEAD)"
- export LAST_TAG=$([[ -n "$PREVIOUS_TAGS" ]] && git merge-base --independent $PREVIOUS_TAGS)
- export VERSION=$([[ -n "$LAST_TAG" ]] && git tag --points-at $LAST_TAG)
- export VERSION=${VERSION:-2.0.0}
- export PATCH=$(git rev-list ${LAST_TAG:+$LAST_TAG..}HEAD | wc -l)
- yarn
......@@ -36,7 +40,7 @@ zip:
xpi-beta:
stage: package
script:
- yarn run manifest:specify -firefox -version $PATCH
- yarn run manifest:specify -firefox -checkver $VERSION -patch $PATCH
- web-ext sign --source-dir=dist --api-key="$SIGN_FF_API_KEY" --api-secret="$SIGN_FF_API_SECRET" --artifacts-dir=dist-zip
artifacts:
paths:
......@@ -54,7 +58,7 @@ xpi-beta:
xpi-unsigned:
stage: package
script:
- yarn run manifest:specify -firefox
- yarn run manifest:specify -firefox -checkver $VERSION
- yarn run build-zip
# do not sign file, has to be manually updated to Mozilla Addons, just prepare it for signing
artifacts:
......@@ -69,7 +73,7 @@ xpi-unsigned:
crx-beta:
stage: package
script:
- yarn run manifest:specify -chrome -version $PATCH
- yarn run manifest:specify -chrome -checkver $VERSION -patch $PATCH
- yarn run build-zip
artifacts:
paths:
......@@ -86,7 +90,7 @@ crx-beta:
crx:
stage: package
script:
- yarn run manifest:specify -chrome
- yarn run manifest:specify -chrome -checkver $VERSION
- yarn run build-zip
artifacts:
paths:
......
......@@ -32,9 +32,9 @@ Google Chrome disabled extensions which are not referenced in their store. All M
If you really want to have it in Chrom(ium/e), you will have to either follow the developer installation or to unzip the crx file and follow the developer installation / chrome instructions. Note that as it will be for development purpose, you may loose your local database doing that...
## Developer installation
First clone this repository locally and install it using `npm install` command or just `yarn` if you prefer (if you don't have npm, [install it](https://www.npmjs.com/get-npm), you will need it or [yarn](https://yarnpkg.com)).
First clone this repository locally and install it using `yarn` (if you don't have yarn, [install it](https://yarnpkg.com)).
Build the extension using npm (note that you will need to run this command when updating extension code) `npm run build:dev` or using yarn `yarn run build:dev`
Build the extension using yarn (note that you will need to run this command when updating extension code) `yarn run build:dev`
This will create the deployable extension in the `dist` folder.
......@@ -48,7 +48,7 @@ Go to [about:debugging#addons](about:debugging#addons) and click on **Load tempo
If you want to debug the extension while testing in Firefox, you will need web-ext. To install :
```
npm i -g web-ext
yarn global add web-ext
```
To load firefox with All Mangas Reader in debug mode, execute the following command in the `dist` folder of your local repository :
```
......
This diff is collapsed.
{
"name": "all-mangas-reader",
"version": "2.0.6",
"version": "2.1.0",
"description": "All Mangas Reader",
"author": "Pierre-Louis Duhoux <pl.duhoux@gmail.com>",
"scripts": {
......@@ -9,8 +9,8 @@
"build:dev-watch": "cross-env NODE_ENV=development webpack --config webpack.config.js --progress --hide-modules --hot --watch",
"build-zip": "node scripts/build-zip.js",
"manifest:specify": "node scripts/update-manifest.js",
"watch": "npm run build -- --watch",
"watch:dev": "npm run build:dev -- --watch"
"watch": "yarn run build --watch",
"watch:dev": "yarn run build:dev --watch"
},
"dependencies": {
"axios": "^0.16.2",
......@@ -19,7 +19,8 @@
"jquery.scrollto": "^2.1.2",
"vue": "^2.5.17",
"vue-gallery": "^1.4.0",
"vuetify": "^1.2.1",
"vue-scrollto": "^2.13.0",
"vuetify": "^1.3.9",
"vuex": "^3.0.1",
"vuex-shared-mutations": "0.0.3",
"webextension-polyfill": "^0.2.1"
......@@ -36,11 +37,11 @@
"uglifyjs-webpack-plugin": "^1.3.0",
"url-loader": "^1.1.1",
"vue-loader": "^15.4.1",
"vue-template-compiler": "^2.5.17",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.5.17",
"webpack": "^4.17.1",
"webpack-cli": "^3.1.0",
"webpack-chrome-extension-reloader": "^0.8.3",
"webpack-cli": "^3.1.0",
"webpack-shell-plugin": "^0.5.0",
"zip-dir": "^1.0.2"
}
......
This diff is collapsed.
This diff is collapsed.
......@@ -26,6 +26,7 @@ export default class {
this.update = obj.update;
if (this.update === undefined || this.update === null) this.update = 1;
this.display = obj.display || 0;
this.layout = obj.layout || 0;
this.cats = obj.cats || [];
if (obj.cats && typeof obj.cats === "string") {
this.cats = JSON.parse(obj.cats);
......@@ -40,5 +41,8 @@ export default class {
this.language = obj.language;
/* all other possible values for this manga langs */
this.languages = obj.languages;
/** Currently reading chapter and page to restart from there if needed */
this.currentChapter = obj.currentChapter;
this.currentScanUrl = obj.currentScanUrl;
}
}
\ No newline at end of file
......@@ -113,7 +113,7 @@ const extractRootDomain = function(url) {
* Extract the part of a url following the domain
* @param {*} url
*/
const afterHostURL = function(url) {
export function afterHostURL(url) {
var after;
//find & remove protocol (http, ftp, etc.) and get hostname
......
......@@ -64,11 +64,15 @@ IconHelper.setBlueIcon();
amrUpdater.load();
// content script included, test if a mirror match the page and load AMR in tab
browser.tabs.onUpdated.addListener((tabId, changeInfo, tabInfo) => {
/*browser.tabs.onUpdated.addListener((tabId, changeInfo, tabInfo) => {
if (changeInfo.status === "loading") { // just load scripts once, when the tab is loading
HandleManga.matchUrlAndLoadScripts(tabInfo.url, tabId)
}
});
});*/
browser.webNavigation.onCommitted.addListener((args) => {
if ("auto_subframe" === args.transitionType) return; // do not reload amr on embedded iframes
HandleManga.matchUrlAndLoadScripts(args.url, args.tabId)
})
/**
* The function below increments the reading of each manga in the list from a chapter each 2 seconds
......
......@@ -28,6 +28,16 @@ class HandleLab {
else if (message.torun === "loadChapterAndDo") {
let res = this.loadChapterAndDo(message, impl);
resolve(res);
}
else if (message.torun === "getScanUrl") {
let img = new Image();
await impl.getImageFromPageAndWrite(message.url, img);
(function wait() {
if (img.src && img.src != "") {
return resolve(img.src);
}
setTimeout(wait, 100);
})()
}
} catch (e) {
reject(e);
......@@ -68,18 +78,9 @@ class HandleLab {
} else if (message.task === "listScans") {
let imagesUrl = await impl.getListImages(document.getElementById(id).contentWindow.document, message.url);
resolve(imagesUrl);
} else if (message.task === "getScanUrl") {
let img = new Image();
await impl.getImageFromPageAndWrite(message.url, img);
(function wait() {
if (img.src && img.src != "") {
resolve(img.src);
}
setTimeout(wait, 100);
})()
} else if (message.task === "wherenav") {
impl.doSomethingBeforeWritingScans(document.getElementById(id).contentWindow.document, message.url);
let where = impl.whereDoIWriteScans(document.getElementById(id).contentWindow.document, message.url);
let where = await impl.whereDoIWriteScans(document.getElementById(id).contentWindow.document, message.url);
resolve(where.length);
}
$("#" + id).remove();
......
......@@ -18,30 +18,55 @@ const contentCss = [
'/lib/jquery.modal.min.css'
];
/** Scripts to inject in pages containing mangas for new reader */
const contentScriptsV2 = [
'/lib/jquery.min.js',
'/reader/init-reading.js'
];
class HandleManga {
handle(message, sender) {
let key;
if (message.url) key = utils.mangaKey(message.url, message.mirror, message.language);
switch (message.action) {
case "mangaExists":
return Promise.resolve(
store.state.mangas.all.find(manga => manga.key === key) !== undefined
);
case "mangaInfos":
let mg = store.state.mangas.all.find(manga => manga.key === key)
if (mg !== undefined) {
return Promise.resolve({
read: mg.read,
display: mg.display
read: mg.read, /* Read top */
display: mg.display, /* Display mode of the old reader */
layout: mg.layout, /* Layout for the new reader */
lastchapter: mg.lastChapterReadURL, /* last read chapter (the most advanced one) */
currentChapter: mg.currentChapter, /* last read chapter, last chapter page opened */
currentScanUrl: mg.currentScanUrl /* last viewed page in currentChapter */
});
} else {
return Promise.resolve();
}
case "saveCurrentState":
return store.dispatch('saveCurrentState', message);
case "readManga":
//count number of chapters read
let nb = localStorage["nb_read"] ? parseInt(localStorage["nb_read"]) : 1
localStorage["nb_read"] = "" + (nb + 1);
utils.debug("Read manga " + message.url);
// call store method to update reading list appropriately
return store.dispatch('readManga', message);
case "getNextChapterImages": //returns list of images for prefetch of next chapter in content script
return this.getChapterImages(message);
case "deleteManga":
utils.debug("Delete manga key " + key);
return store.dispatch('deleteManga', {key: key});
case "getNextChapterImages", "getChapterData": //returns boolean telling if url is a chapter page, infos from page and list of images for prefetch of next chapter in content script
return this.getChapterData(message);
case "markReadTop":
return store.dispatch('markMangaReadTop', message);
case "setDisplayMode":
return store.dispatch('setMangaDisplayMode', message);
case "setLayoutMode":
return store.dispatch('setMangaLayoutMode', message);
case "setMangaChapter":
return store.dispatch('resetManga', message) // reset reading to first chapter
.then(() => store.dispatch('readManga', message)); // set reading to current chapter
......@@ -159,15 +184,58 @@ class HandleManga {
const mir = utils.currentPageMatch(url)
if (mir === null) return Promise.resolve(null)
if (!localStorage["oldreader"]) {
// check if we need to load preload (it could be annoying to have preload on each pages of the website)
// websites which provide a chapter_url regexp will have their chapters with a preload
let dopreload = false
if (mir.chapter_url) {
var parts = /\/(.*)\/(.*)/.exec(mir.chapter_url);
var chaprx = new RegExp(parts[1], parts[2]);
if (chaprx.test("/" + utils.afterHostURL(url))) dopreload = true
}
if (dopreload) {
// Load amr preload
let loading = []
loading.push(browser.tabs.insertCSS(tabId, { file: "/reader/pre-loader.css" }))
let bgcolor = "#424242"
if (store.state.options.darkreader === 0) bgcolor = "white"
loading.push(browser.tabs.executeScript(
tabId,
{ code: `
let amr_icon_url = '${browser.extension.getURL('/icons/icon_128.png')}';
let cover = document.createElement("div")
cover.id = "amr-loading-cover"
cover.style.backgroundColor = "${bgcolor}"
let img = document.createElement("img")
img.src = amr_icon_url;
cover.appendChild(img)
document.body.appendChild(cover)
setTimeout(() => {
try {cover.parentNode.remove(cover)} catch(e) {}
}, 5000)
`}))
Promise.all(loading)
}
}
let impl = await this.getImplementation(mir)
if (impl) {
// Inject css in matched tab
for (let css of contentCss) {
await browser.tabs.insertCSS(tabId, { file: css });
}
// Inject content scripts in matched tab
for (let script of contentScripts) {
await browser.tabs.executeScript(tabId, { file: script });
if (localStorage["oldreader"]) {
// Inject css in matched tab
for (let css of contentCss) {
await browser.tabs.insertCSS(tabId, { file: css });
}
// Inject content scripts in matched tab
for (let script of contentScripts) {
await browser.tabs.executeScript(tabId, { file: script });
}
} else {
// Inject content scripts in matched tab
for (let script of contentScriptsV2) {
await browser.tabs.executeScript(tabId, { file: script });
}
}
// Inject mirror implementation (through a function called in the implementation and existing in back.js)
await browser.tabs.executeScript(tabId, { code: impl });
......@@ -179,7 +247,7 @@ class HandleManga {
* Return the list of images urls from a chapter
* @param {*} message
*/
async getChapterImages(message) {
async getChapterData(message) {
return Axios.get(message.url)
.then(resp => {
return new Promise((resolve, reject) => {
......@@ -197,12 +265,38 @@ class HandleManga {
let ldoc = document.getElementById(id).contentWindow.document;
ldoc.documentElement.innerHTML = resp.data;
let readyCall = async () => {
// loads the implementation code
let impl = await mirrorsImpl.getImpl(message.mirrorName);
var imagesUrl = await impl.getListImages(document.getElementById(id).contentWindow.document, message.url);
// Check if this is a chapter page
let isChapter = impl.isCurrentPageAChapterPage(
document.getElementById(id).contentWindow.document,
message.url)
let infos, imagesUrl = []
if (isChapter) {
try {
// Retrieve informations relative to current chapter / manga read
infos = await impl.getInformationsFromCurrentPage(
document.getElementById(id).contentWindow.document,
message.url)
// retrieve images to load
imagesUrl = await impl.getListImages(
document.getElementById(id).contentWindow.document,
message.url);
} catch (e) {
console.error("Error while loading infos and images from url " + message.url)
console.error(e)
}
}
let title = document.getElementById(id).contentWindow.document.title
$("#" + id).remove();
resolve({
images: imagesUrl
isChapter: isChapter,
infos: infos,
images: imagesUrl,
title: title
});
$("#" + id).remove();
}
if (ldoc.readyState === "complete" ||
(ldoc.readyState !== "loading" && !ldoc.documentElement.doScroll)) {
......
import browser from "webextension-polyfill";
import store from '../store';
class HandleMisc {
handle(message, sender) {
......@@ -9,6 +10,16 @@ class HandleMisc {
"url": message.url
});
return Promise.resolve();
case "mirrorInfos":
let mirror = store.state.mirrors.all.find(mir => mir.mirrorName === message.name)
return Promise.resolve({ // can't send a vuex object through js instances on Firefox --> convert
activated: mirror.activated,
domains: mirror.domains,
home: mirror.home,
languages: mirror.languages,
mirrorIcon: mirror.mirrorIcon,
mirrorName: mirror.mirrorName
});
}
}
}
......
import store from '../store';
class HandleNavigation {
handle(message, sender) {
switch (message.action) {
// Set bar state
case "setBarState":
localStorage.isBarVisible = message.barstate;
return Promise.resolve({});
// hide navigation bar --> keeps its state
case "hideBar":
if (localStorage.isBarVisible == 1) {
localStorage.isBarVisible = 0;
} else {
localStorage.isBarVisible = 1;
}
localStorage.isBarVisible = (localStorage.isBarVisible + 1) % 2
return Promise.resolve({
res: localStorage.isBarVisible
});
......@@ -17,15 +19,20 @@ class HandleNavigation {
return Promise.resolve({});
// get current state of navigation bar
case "barState":
if (localStorage.isBarVisible === undefined) {
return Promise.resolve({
barVis: 1
});
} else {
return Promise.resolve({
barVis: localStorage.isBarVisible
});
}
return Promise.resolve({
barVis: localStorage.isBarVisible === undefined ? 1 : localStorage.isBarVisible
});
// get a value from localStorage
case "get_storage":
return Promise.resolve(localStorage[message.key]);
// set a Value in localStorage
case "set_storage":
localStorage[message.key] = message.value;
return Promise.resolve();
// set a Value in localStorage
case "save_option":
store.dispatch("setOption", { key: message.key, value: message.value });
return Promise.resolve();
}
}
}
......
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<script src="./index.js"></script>
</body>
</html>
\ No newline at end of file
/**
* This file is exported to /backup/index.js
* It renders a json file of the reading list
* Just go to chrome-extensions://..../backup/index.html or firefox equivalent
*/
import store from '../store';
const exportFile = function () {
let mgs = store.state.mangas.all;
mgs = mgs.map(mg => {
let res = {
m: mg.mirror,
n: mg.name,
u: mg.url,
l: mg.lastChapterReadURL
};
if (mg.read !== 0) res.r = mg.read;
if (mg.update !== 1) res.p = mg.update;
if (mg.display !== 0) res.d = mg.display;
if (mg.layout !== 0) res.y = mg.layout;
if (mg.cats.length > 0) res.c = mg.cats;
if (mg.language !== undefined) res.g = mg.language;
return res;
});
let exp = { mangas: mgs };
//add bookmarks
let bms = store.state.bookmarks.all;
bms = bms.map(bm => {
let res = {
m: bm.mirror,
n: bm.name,
u: bm.url,
c: bm.chapUrl,
h: bm.chapName,
o: bm.note,
t: bm.type
}
if (bm.type === "scan") {
res.s = bm.scanUrl;
res.a = bm.scanName;
}
return res;
});
exp.bookmarks = bms;
// output formatted json
document.write(JSON.stringify(exp, null, 2))
}
;(async function () {
// Load options in store before everything
await store.dispatch("getStateFromReference", {
module: "mangas",
key: "all",
mutation: "setMangas"
})
await store.dispatch("getStateFromReference", {
module: "bookmarks",
key: "all",
mutation: "setBookmarks"
})
/** output json file */
exportFile()
})();
/**
* DEPRECATED
* This code is part of the old version of the reader inherited from V1
* It still contains code using jQuery or importing code using jQuery
* The new reader can be found in the reader folder and is full VueJS
*
* This code is kept for debugging reasons and "just in case"
* It will be removed from V2.5
*/
/**
* All Mangas Reader V2
* Content script included in every website matching a manga site
......@@ -56,7 +66,7 @@ if (window["__backamr__"] === undefined) { // avoid loading script twice
reading.createBook(imagesUrl);
// mark manga as read
if (options.markwhendownload === 0 && options.addauto == 1) {
if (options.markwhendownload === 0) {
reading.consultManga();
}
......
/**
* DEPRECATED
* This code is part of the old version of the reader inherited from V1
* It still contains code using jQuery or importing code using jQuery
* The new reader can be found in the reader folder and is full VueJS
*
* This code is kept for debugging reasons and "just in case"
* It will be removed from V2.5
*/
/**
This file is part of All Mangas Reader.
......
/**
* DEPRECATED
* This code is part of the old version of the reader inherited from V1
* It still contains code using jQuery or importing code using jQuery
* The new reader can be found in the reader folder and is full VueJS
*
* This code is kept for debugging reasons and "just in case"
* It will be removed from V2.5
*/
import options from "./options";
class HandleKey {
......
/**
* DEPRECATED
* This code is part of the old version of the reader inherited from V1
* It still contains code using jQuery or importing code using jQuery
* The new reader can be found in the reader folder and is full VueJS
*
* This code is kept for debugging reasons and "just in case"
* It will be removed from V2.5
*/
class MirrorImpl {
constructor() {
/**
......
/**
* DEPRECATED
* This code is part of the old version of the reader inherited from V1
* It still contains code using jQuery or importing code using jQuery
* The new reader can be found in the reader folder and is full VueJS
*
* This code is kept for debugging reasons and "just in case"
* It will be removed from V2.5
*/
import browser from "webextension-polyfill";
import options from './options';
import mirrorImpl from './mirrorimpl';
......
/**
* DEPRECATED
* This code is part of the old version of the reader inherited from V1
* It still contains code using jQuery or importing code using jQuery
* The new reader can be found in the reader folder and is full VueJS
*
* This code is kept for debugging reasons and "just in case"
* It will be removed from V2.5
*/
/**
* Class to retrieve AMR options
*/
......
/**
* DEPRECATED
* This code is part of the old version of the reader inherited from V1
* It still contains code using jQuery or importing code using jQuery
* The new reader can be found in the reader folder and is full VueJS
*
* This code is kept for debugging reasons and "just in case"
* It will be removed from V2.5
*/
import Vue from "vue";
/**
* Encapsulate data collected from mirror implementation to be retrieved everywhere
*/
......@@ -14,7 +26,7 @@ class PageData {
this.__data__ = object;
}
add(key, value) {
this[key] = value;
Vue.set(this, key, value) /* Properties added to pageData are reactive in Vue components */
}
}
export default (new PageData)
\ No newline at end of file
/**
* DEPRECATED
* This code is part of the old version of the reader inherited from V1
* It still contains code using jQuery or importing code using jQuery
* The new reader can be found in the reader folder and is full VueJS
*
* This code is kept for debugging reasons and "just in case"
* It will be removed from V2.5
*/
import browser from "webextension-polyfill";
import options from './options';
import mirrorImpl from './mirrorimpl'
......@@ -7,7 +17,18 @@ import i18n from '../amr/i18n';
import mirrorHelper from '../amr/mirrors-helper';
class Reading {
consultManga() {
async consultManga() {
if (options.addauto !== 1) { // check if option "Automatically add manga to list" is unchecked
// check if manga is already in list
let exists = await browser.runtime.sendMessage({
action: "mangaExists",
url: pageData.currentMangaURL,
mirror: mirrorImpl.get().mirrorName,
language: pageData.language
})
// if not, we do not add the manga to the list (else, we continue, so reading progress is updated)
if (!exists) return;
}
browser.runtime.sendMessage({
action: "readManga",
url: pageData.currentMangaURL,
......@@ -21,7 +42,7 @@ class Reading {
async createBook(imagesUrl) {
if (options.displayChapters == 1) { // display as a book
let where = mirrorImpl.get().whereDoIWriteScans(document, window.location.href);
let where = await mirrorImpl.get().whereDoIWriteScans(document, window.location.href);
pageData.whereScans = where;
//Get specific mode for currentManga
let curmode = -1;
......@@ -318,7 +339,7 @@ class Reading {
if (pageData.nexturltoload && options.prefetch == 1) {
this.loadNextChapter(pageData.nexturltoload);
}
if (options.markwhendownload === 1 && options.addauto === 1) {
if (options.markwhendownload === 1) {
this.consultManga();
}
} else {
......