Compare commits
No commits in common. "develop" and "0.2.1.0-beta.0" have entirely different histories.
develop
...
0.2.1.0-be
14
.github/dependabot.yml
vendored
14
.github/dependabot.yml
vendored
|
@ -1,14 +0,0 @@
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: npm
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: daily
|
|
||||||
time: "04:00"
|
|
||||||
open-pull-requests-limit: 10
|
|
||||||
target-branch: develop
|
|
||||||
reviewers:
|
|
||||||
- Serraniel
|
|
||||||
assignees:
|
|
||||||
- Serraniel
|
|
||||||
versioning-strategy: increase
|
|
15
.github/workflows/auto-merge.yml
vendored
15
.github/workflows/auto-merge.yml
vendored
|
@ -1,15 +0,0 @@
|
||||||
name: auto-merge
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
auto-merge:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.actor == 'dependabot[bot]'
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: ahmadnassri/action-dependabot-auto-merge@v2.3.1
|
|
||||||
with:
|
|
||||||
target: minor
|
|
||||||
github-token: ${{ secrets.PR_AUTO_MERGE }}
|
|
|
@ -1,6 +1,6 @@
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js:
|
node_js:
|
||||||
- "16"
|
- "node"
|
||||||
dist: trusty
|
dist: trusty
|
||||||
cache:
|
cache:
|
||||||
npm: true
|
npm: true
|
||||||
|
|
14
README.md
14
README.md
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
[![Travis (.org)](https://img.shields.io/travis/serraniel/aniwatchplus?style=flat-square)](https://travis-ci.org/github/Serraniel/AniwatchPlus)
|
[![Travis (.org)](https://img.shields.io/travis/serraniel/aniwatchplus?style=flat-square)](https://travis-ci.org/github/Serraniel/AniwatchPlus)
|
||||||
[![Snyk Vulnerabilities for GitHub Repo](https://img.shields.io/snyk/vulnerabilities/github/serraniel/aniwatchplus?style=flat-square)](https://snyk.io/test/github/Serraniel/AniwatchPlus?targetFile=package.json)
|
[![Snyk Vulnerabilities for GitHub Repo](https://img.shields.io/snyk/vulnerabilities/github/serraniel/aniwatchplus?style=flat-square)](https://snyk.io/test/github/Serraniel/AniwatchPlus?targetFile=package.json)
|
||||||
|
[![David](https://img.shields.io/david/serraniel/AniwatchPlus?style=flat-square)](https://david-dm.org/serraniel/aniwatchplus)
|
||||||
[![Scrutinizer code quality (GitHub/Bitbucket)](https://img.shields.io/scrutinizer/quality/g/serraniel/aniwatchplus?style=flat-square)](https://scrutinizer-ci.com/g/Serraniel/AniwatchPlus/)
|
[![Scrutinizer code quality (GitHub/Bitbucket)](https://img.shields.io/scrutinizer/quality/g/serraniel/aniwatchplus?style=flat-square)](https://scrutinizer-ci.com/g/Serraniel/AniwatchPlus/)
|
||||||
[![GitHub issues](https://img.shields.io/github/issues/serraniel/aniwatchplus?style=flat-square)](https://github.com/Serraniel/AniwatchPlus/issues)
|
[![GitHub issues](https://img.shields.io/github/issues/serraniel/aniwatchplus?style=flat-square)](https://github.com/Serraniel/AniwatchPlus/issues)
|
||||||
[![GitHub](https://img.shields.io/github/license/serraniel/aniwatchplus?style=flat-square)](https://github.com/Serraniel/AniwatchPlus/blob/develop/LICENSE)
|
[![GitHub](https://img.shields.io/github/license/serraniel/aniwatchplus?style=flat-square)](https://github.com/Serraniel/AniwatchPlus/blob/develop/LICENSE)
|
||||||
|
@ -11,17 +12,12 @@
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
* Adds quick search
|
* Adds quick search
|
||||||
* Time conversion into localtime
|
|
||||||
* Cleaner list presentation
|
* Cleaner list presentation
|
||||||
* Improved presentation of available audio and subtitles
|
* Improved presentation of available audio and subtitles
|
||||||
* Better display of anime requests
|
* Better display of anime requests
|
||||||
* Display notification count in tab title
|
|
||||||
* Autoplay after screenshot
|
|
||||||
|
|
||||||
## Download
|
## Download
|
||||||
Click the badge for your browser to get to the download page:
|
[![Chrome Web Store](https://img.shields.io/chrome-web-store/v/hgniihpjiioldkafogebpkbaiflmpimb?label=Google%20Chrome&logo=Google%20Chrome&style=flat-square)](https://chrome.google.com/webstore/detail/aniwatch-plus/hgniihpjiioldkafogebpkbaiflmpimb?hl=de)
|
||||||
|
|
||||||
[![Chrome Web Store](https://img.shields.io/chrome-web-store/v/hgniihpjiioldkafogebpkbaiflmpimb?label=Google%20Chrome&logo=Google%20Chrome&style=flat-square)](https://chrome.google.com/webstore/detail/aniwatch-plus/hgniihpjiioldkafogebpkbaiflmpimb)
|
|
||||||
[![Mozilla Add-on](https://img.shields.io/amo/v/aniwatch-plus?label=Mozilla%20Firefox&logo=Firefox&style=flat-square)](https://addons.mozilla.org/de/firefox/addon/aniwatch-plus/?utm_source=addons.mozilla.org&utm_medium=referral&utm_content=search)
|
[![Mozilla Add-on](https://img.shields.io/amo/v/aniwatch-plus?label=Mozilla%20Firefox&logo=Firefox&style=flat-square)](https://addons.mozilla.org/de/firefox/addon/aniwatch-plus/?utm_source=addons.mozilla.org&utm_medium=referral&utm_content=search)
|
||||||
[![GitHub release tag for opera (latest by date including pre-releases)](https://img.shields.io/github/v/release/serraniel/aniwatchplus?include_prereleases&label=Opera&logo=Opera&logoColor=red&style=flat-square)](https://addons.opera.com/de/extensions/details/aniwatch-plus/)
|
[![GitHub release tag for opera (latest by date including pre-releases)](https://img.shields.io/github/v/release/serraniel/aniwatchplus?include_prereleases&label=Opera&logo=Opera&logoColor=red&style=flat-square)](https://addons.opera.com/de/extensions/details/aniwatch-plus/)
|
||||||
[![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/serraniel/aniwatchplus?include_prereleases&label=Download%20manually&logo=Github&style=flat-square)](https://github.com/Serraniel/AniwatchPlus/releases)
|
[![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/serraniel/aniwatchplus?include_prereleases&label=Download%20manually&logo=Github&style=flat-square)](https://github.com/Serraniel/AniwatchPlus/releases)
|
||||||
|
@ -32,9 +28,9 @@ This project requires you to install the latestst versions of [Node.js](https://
|
||||||
Minimum required versions:
|
Minimum required versions:
|
||||||
| Tool | Version |
|
| Tool | Version |
|
||||||
|-|-|
|
|-|-|
|
||||||
| node.js | ^14.x.x |
|
| node.js | => 12.18.x |
|
||||||
| npm | ^6.x.x |
|
| npm | => 6.14.x |
|
||||||
| gulp | ^4.x.x |
|
| gulp | => 4.0.x |
|
||||||
|
|
||||||
|
|
||||||
### Build
|
### Build
|
||||||
|
|
74
gulpfile.js
74
gulpfile.js
|
@ -4,18 +4,15 @@ const gulpLoadPlugins = require('gulp-load-plugins')
|
||||||
const terser = require('terser');
|
const terser = require('terser');
|
||||||
const del = require('del');
|
const del = require('del');
|
||||||
const browserify = require('browserify');
|
const browserify = require('browserify');
|
||||||
const tsify = require('tsify')
|
|
||||||
const babelify = require('babelify');
|
const babelify = require('babelify');
|
||||||
const source = require('vinyl-source-stream');
|
const source = require('vinyl-source-stream');
|
||||||
const buffer = require('vinyl-buffer');
|
const buffer = require('vinyl-buffer');
|
||||||
const merge = require('merge-stream');
|
const merge = require('merge-stream');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const factor = require('factor-bundle');
|
|
||||||
const { debug } = require('console');
|
|
||||||
|
|
||||||
const $ = gulpLoadPlugins()
|
const $ = gulpLoadPlugins()
|
||||||
|
|
||||||
const sass = require('gulp-sass')(require('sass'));
|
$.sass.compiler = require('sass');
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
Base consts
|
Base consts
|
||||||
|
@ -28,7 +25,6 @@ const src = {
|
||||||
styles: 'src/stylesheets',
|
styles: 'src/stylesheets',
|
||||||
scripts: 'src/javascript',
|
scripts: 'src/javascript',
|
||||||
images: 'src/images',
|
images: 'src/images',
|
||||||
html: 'src/html',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build path
|
// Build path
|
||||||
|
@ -38,7 +34,6 @@ const tmp = {
|
||||||
styles: '.tmp/stylesheets',
|
styles: '.tmp/stylesheets',
|
||||||
scripts: '.tmp/javascript',
|
scripts: '.tmp/javascript',
|
||||||
images: '.tmp/images',
|
images: '.tmp/images',
|
||||||
html: '.tmp/html',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dist path
|
// Dist path
|
||||||
|
@ -49,21 +44,18 @@ const dist = {
|
||||||
styles: 'dist/chrome/stylesheets',
|
styles: 'dist/chrome/stylesheets',
|
||||||
scripts: 'dist/chrome/javascript',
|
scripts: 'dist/chrome/javascript',
|
||||||
images: 'dist/chrome/images',
|
images: 'dist/chrome/images',
|
||||||
html: 'dist/chrome/html',
|
|
||||||
},
|
},
|
||||||
firefox: {
|
firefox: {
|
||||||
root: 'dist/firefox',
|
root: 'dist/firefox',
|
||||||
styles: 'dist/firefox/stylesheets',
|
styles: 'dist/firefox/stylesheets',
|
||||||
scripts: 'dist/firefox/javascript',
|
scripts: 'dist/firefox/javascript',
|
||||||
images: 'dist/firefox/images',
|
images: 'dist/firefox/images',
|
||||||
html: 'dist/firefox/html',
|
|
||||||
},
|
},
|
||||||
opera: {
|
opera: {
|
||||||
root: 'dist/opera',
|
root: 'dist/opera',
|
||||||
styles: 'dist/opera/stylesheets',
|
styles: 'dist/opera/stylesheets',
|
||||||
scripts: 'dist/opera/javascript',
|
scripts: 'dist/opera/javascript',
|
||||||
images: 'dist/opera/images',
|
images: 'dist/opera/images',
|
||||||
html: 'dist/opera/html',
|
|
||||||
},
|
},
|
||||||
zip: 'dist/zips',
|
zip: 'dist/zips',
|
||||||
}
|
}
|
||||||
|
@ -82,11 +74,11 @@ gulp.task('styles', () => {
|
||||||
// sourcemap initialization
|
// sourcemap initialization
|
||||||
.pipe($.if(isDev, $.sourcemaps.init()))
|
.pipe($.if(isDev, $.sourcemaps.init()))
|
||||||
// sass compilation
|
// sass compilation
|
||||||
.pipe(sass.sync({
|
.pipe($.sass.sync({
|
||||||
outputStyle: 'expanded',
|
outputStyle: 'expanded',
|
||||||
precision: 7,
|
precision: 7,
|
||||||
includePaths: ['.'],
|
includePaths: ['.'],
|
||||||
}).on('error', sass.logError))
|
}).on('error', $.sass.logError))
|
||||||
// autoprefix
|
// autoprefix
|
||||||
.pipe($.autoprefixer())
|
.pipe($.autoprefixer())
|
||||||
// out stream size
|
// out stream size
|
||||||
|
@ -116,34 +108,14 @@ gulp.task('styles', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
gulp.task('scripts', () => {
|
gulp.task('scripts', () => {
|
||||||
const modules = [
|
let b = browserify({
|
||||||
'app',
|
entries: `${src.scripts}/index.js`,
|
||||||
'settings',
|
debug: isDev
|
||||||
];
|
|
||||||
|
|
||||||
const inputs = [];
|
|
||||||
const streams = [];
|
|
||||||
|
|
||||||
modules.forEach(module => {
|
|
||||||
inputs.push(`${src.scripts}/${module}.ts`);
|
|
||||||
streams.push(source(`${module}.js`));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const b = browserify(inputs, { debug: isDev });
|
return b.transform('babelify').bundle()
|
||||||
|
|
||||||
let outstream = b
|
|
||||||
.plugin(tsify)
|
|
||||||
.transform('babelify')
|
|
||||||
.plugin(factor, { outputs: streams })
|
|
||||||
.bundle()
|
|
||||||
.pipe(source('common.js'))
|
|
||||||
|
|
||||||
streams.forEach(stream => {
|
|
||||||
outstream = outstream.pipe($.merge(stream));
|
|
||||||
});
|
|
||||||
|
|
||||||
return outstream
|
|
||||||
.pipe($.plumber())
|
.pipe($.plumber())
|
||||||
|
.pipe(source('app.js'))
|
||||||
.pipe(buffer())
|
.pipe(buffer())
|
||||||
.pipe($.if(isDev, $.sourcemaps.init({ loadMaps: true })))
|
.pipe($.if(isDev, $.sourcemaps.init({ loadMaps: true })))
|
||||||
.pipe($.terser({ compress: { drop_console: isProd, drop_debugger: isProd } }))
|
.pipe($.terser({ compress: { drop_console: isProd, drop_debugger: isProd } }))
|
||||||
|
@ -152,7 +124,7 @@ gulp.task('scripts', () => {
|
||||||
showFiles: true,
|
showFiles: true,
|
||||||
}))
|
}))
|
||||||
.pipe($.if(isDev, $.sourcemaps.write()))
|
.pipe($.if(isDev, $.sourcemaps.write()))
|
||||||
.pipe(gulp.dest(`${tmp.scripts}`));
|
.pipe(gulp.dest(`${tmp.scripts}`))
|
||||||
})
|
})
|
||||||
|
|
||||||
gulp.task('images', () => {
|
gulp.task('images', () => {
|
||||||
|
@ -170,16 +142,6 @@ gulp.task('images', () => {
|
||||||
.pipe(gulp.dest(tmp.images))
|
.pipe(gulp.dest(tmp.images))
|
||||||
})
|
})
|
||||||
|
|
||||||
gulp.task('html', () => {
|
|
||||||
return gulp.src(`${src.html}/**/*`)
|
|
||||||
.pipe($.plumber())
|
|
||||||
// any steps for HTML processing?
|
|
||||||
.pipe($.size({
|
|
||||||
showFiles: true,
|
|
||||||
}))
|
|
||||||
.pipe(gulp.dest(tmp.html))
|
|
||||||
})
|
|
||||||
|
|
||||||
gulp.task('manifests', () => {
|
gulp.task('manifests', () => {
|
||||||
const templateFile = `${src.manifests}/manifest.template.json`;
|
const templateFile = `${src.manifests}/manifest.template.json`;
|
||||||
|
|
||||||
|
@ -208,12 +170,10 @@ Watchers
|
||||||
gulp.task('watch', (done) => {
|
gulp.task('watch', (done) => {
|
||||||
gulp.watch(`${src.styles}/**/*.scss`, gulp.series('clean:build', 'styles', 'dist:copy', 'dist:zip'))
|
gulp.watch(`${src.styles}/**/*.scss`, gulp.series('clean:build', 'styles', 'dist:copy', 'dist:zip'))
|
||||||
|
|
||||||
gulp.watch(`${src.scripts}/**/*.ts`, gulp.series('clean:build', 'scripts', 'dist:copy', 'dist:zip'))
|
gulp.watch(`${src.scripts}/**/*.js`, gulp.series('clean:build', 'scripts', 'dist:copy', 'dist:zip'))
|
||||||
|
|
||||||
gulp.watch(`${src.images}/**/*`, gulp.series('clean:build', 'images', 'dist:copy', 'dist:zip'))
|
gulp.watch(`${src.images}/**/*`, gulp.series('clean:build', 'images', 'dist:copy', 'dist:zip'))
|
||||||
|
|
||||||
gulp.watch(`${src.html}/**/*`, gulp.series('clean:build', 'html', 'dist:copy', 'dist:zip'))
|
|
||||||
|
|
||||||
gulp.watch(`${src.manifests}/**/*.*`, gulp.series('clean:build', 'manifests', 'dist:copy', 'dist:zip'))
|
gulp.watch(`${src.manifests}/**/*.*`, gulp.series('clean:build', 'manifests', 'dist:copy', 'dist:zip'))
|
||||||
|
|
||||||
done();
|
done();
|
||||||
|
@ -233,7 +193,7 @@ gulp.task('clean', gulp.series('clean:build', 'clean:dist'))
|
||||||
BUILD CLEAN ALL
|
BUILD CLEAN ALL
|
||||||
============================================================================ */
|
============================================================================ */
|
||||||
|
|
||||||
gulp.task('build', gulp.series('manifests', 'images', 'scripts', 'styles', 'html'));
|
gulp.task('build', gulp.series('manifests', 'images', 'scripts', 'styles'));
|
||||||
|
|
||||||
gulp.task('build:clean', gulp.series('clean:build', 'manifests', 'images', 'scripts', 'styles'));
|
gulp.task('build:clean', gulp.series('clean:build', 'manifests', 'images', 'scripts', 'styles'));
|
||||||
|
|
||||||
|
@ -255,10 +215,6 @@ gulp.task('dist:chrome', (done) => {
|
||||||
gulp.src(`${tmp.styles}/*.{min.css,min.css.gz}`)
|
gulp.src(`${tmp.styles}/*.{min.css,min.css.gz}`)
|
||||||
.pipe(gulp.dest(dist.chrome.styles)),
|
.pipe(gulp.dest(dist.chrome.styles)),
|
||||||
|
|
||||||
// copy html
|
|
||||||
gulp.src(`${tmp.html}/*.html`)
|
|
||||||
.pipe(gulp.dest(dist.chrome.html)),
|
|
||||||
|
|
||||||
gulp.src(`${tmp.manifests}/chrome*`)
|
gulp.src(`${tmp.manifests}/chrome*`)
|
||||||
.pipe($.rename('manifest.json'))
|
.pipe($.rename('manifest.json'))
|
||||||
.pipe(gulp.dest(dist.chrome.root))
|
.pipe(gulp.dest(dist.chrome.root))
|
||||||
|
@ -279,10 +235,6 @@ gulp.task('dist:firefox', (done) => {
|
||||||
gulp.src(`${tmp.styles}/*.{min.css,min.css.gz}`)
|
gulp.src(`${tmp.styles}/*.{min.css,min.css.gz}`)
|
||||||
.pipe(gulp.dest(dist.firefox.styles)),
|
.pipe(gulp.dest(dist.firefox.styles)),
|
||||||
|
|
||||||
// copy html
|
|
||||||
gulp.src(`${tmp.html}/*.html`)
|
|
||||||
.pipe(gulp.dest(dist.firefox.html)),
|
|
||||||
|
|
||||||
gulp.src(`${tmp.manifests}/firefox*`)
|
gulp.src(`${tmp.manifests}/firefox*`)
|
||||||
.pipe($.rename('manifest.json'))
|
.pipe($.rename('manifest.json'))
|
||||||
.pipe(gulp.dest(dist.firefox.root))
|
.pipe(gulp.dest(dist.firefox.root))
|
||||||
|
@ -303,10 +255,6 @@ gulp.task('dist:opera', (done) => {
|
||||||
gulp.src(`${tmp.styles}/*.{min.css,min.css.gz}`)
|
gulp.src(`${tmp.styles}/*.{min.css,min.css.gz}`)
|
||||||
.pipe(gulp.dest(dist.opera.styles)),
|
.pipe(gulp.dest(dist.opera.styles)),
|
||||||
|
|
||||||
// copy html
|
|
||||||
gulp.src(`${tmp.html}/*.html`)
|
|
||||||
.pipe(gulp.dest(dist.opera.html)),
|
|
||||||
|
|
||||||
gulp.src(`${tmp.manifests}/opera*`)
|
gulp.src(`${tmp.manifests}/opera*`)
|
||||||
.pipe($.rename('manifest.json'))
|
.pipe($.rename('manifest.json'))
|
||||||
.pipe(gulp.dest(dist.opera.root))
|
.pipe(gulp.dest(dist.opera.root))
|
||||||
|
|
21698
package-lock.json
generated
21698
package-lock.json
generated
File diff suppressed because it is too large
Load diff
61
package.json
61
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "aniwatch-plus",
|
"name": "aniwatch-plus",
|
||||||
"version": "1.1.0",
|
"version": "0.2.1-beta.0",
|
||||||
"description": "Aniwatch Plus is a browser extension for https://aniwatch.me/",
|
"description": "Aniwatch Plus is a browser extension for https://aniwatch.me/",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -32,51 +32,40 @@
|
||||||
"email": "mail@serraniel.dev"
|
"email": "mail@serraniel.dev"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/Serraniel/AniwatchPlus#readme",
|
"homepage": "https://github.com/Serraniel/AniwatchPlus#readme",
|
||||||
"dependencies": {
|
"dependencies": {},
|
||||||
"color": "^3.2.1",
|
|
||||||
"spacetime": "https://github.com/Serraniel/spacetime#bugfix/#255-typescript-constructor-options-missing",
|
|
||||||
"uuid": "^8.3.2"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/compat-data": "^7.22.5",
|
"@babel/compat-data": "^7.11.0",
|
||||||
"@babel/core": "^7.22.9",
|
"@babel/core": "^7.11.4",
|
||||||
"@babel/helper-module-imports": "^7.22.5",
|
"@babel/helper-module-imports": "^7.10.4",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.17.12",
|
"@babel/plugin-proposal-class-properties": "^7.10.4",
|
||||||
"@babel/plugin-proposal-private-methods": "^7.17.12",
|
"@babel/plugin-proposal-private-methods": "^7.10.4",
|
||||||
"@babel/preset-env": "^7.22.9",
|
"@babel/preset-env": "^7.11.0",
|
||||||
"@babel/register": "^7.22.5",
|
"@babel/register": "^7.10.5",
|
||||||
"@types/chrome": "0.0.241",
|
|
||||||
"babelify": "^10.0.0",
|
"babelify": "^10.0.0",
|
||||||
"browserify": "^17.0.0",
|
"browserify": "^17.0.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.2",
|
||||||
"cssnano": "^5.1.15",
|
"cssnano": "^4.1.10",
|
||||||
"del": "^6.1.1",
|
"del": "^6.0.0",
|
||||||
"factor-bundle": "^2.5.0",
|
|
||||||
"gulp": "^4.0.2",
|
"gulp": "^4.0.2",
|
||||||
"gulp-autoprefixer": "^8.0.0",
|
"gulp-autoprefixer": "^7.0.1",
|
||||||
"gulp-babel": "^8.0.0",
|
"gulp-babel": "^8.0.0",
|
||||||
"gulp-if": "^3.0.0",
|
"gulp-if": "^3.0.0",
|
||||||
"gulp-imagemin": "^7.1.0",
|
"gulp-imagemin": "^7.1.0",
|
||||||
"gulp-load-plugins": "^2.0.8",
|
"gulp-load-plugins": "^2.0.5",
|
||||||
"gulp-merge": "^0.1.1",
|
|
||||||
"gulp-plumber": "^1.2.1",
|
"gulp-plumber": "^1.2.1",
|
||||||
"gulp-postcss": "^9.0.1",
|
"gulp-postcss": "^9.0.0",
|
||||||
"gulp-rename": "^2.0.0",
|
"gulp-rename": "^2.0.0",
|
||||||
"gulp-replace": "^1.1.4",
|
"gulp-replace": "^1.0.0",
|
||||||
"gulp-sass": "^5.1.0",
|
"gulp-sass": "^4.1.0",
|
||||||
"gulp-size": "^4.0.1",
|
"gulp-size": "^3.0.0",
|
||||||
"gulp-sourcemaps": "^3.0.0",
|
"gulp-sourcemaps": "^2.6.5",
|
||||||
"gulp-terser": "^2.1.0",
|
"gulp-terser": "^1.4.0",
|
||||||
"gulp-typescript": "^6.0.0-alpha.1",
|
"gulp-zip": "^5.0.2",
|
||||||
"gulp-zip": "^5.1.0",
|
|
||||||
"merge-stream": "^2.0.0",
|
"merge-stream": "^2.0.0",
|
||||||
"postcss": "^8.4.27",
|
"postcss": "^8.1.1",
|
||||||
"sass": "^1.64.1",
|
"sass": "^1.27.0",
|
||||||
"terser": "^5.19.2",
|
"terser": "^5.3.4",
|
||||||
"tsify": "^5.0.4",
|
|
||||||
"typescript": "^4.9.5",
|
|
||||||
"vinyl-buffer": "^1.0.1",
|
"vinyl-buffer": "^1.0.1",
|
||||||
"vinyl-source-stream": "^2.0.0",
|
"vinyl-source-stream": "^2.0.0"
|
||||||
"web-ext-types": "^3.2.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<title>Aniwatch Plus Settings</title>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<form>
|
|
||||||
<h1>Aniwatch Plus Settings</h1>
|
|
||||||
|
|
||||||
<h2>General Settings</h2>
|
|
||||||
<hr />
|
|
||||||
<h3>Website</h3>
|
|
||||||
<input type="checkbox" id="websiteDisplayQuickSearch" data-default-value="true" />
|
|
||||||
<label for="websiteDisplayQuickSearch">Enable Quick Search</label><br />
|
|
||||||
|
|
||||||
<input type="checkbox" id="websiteAutoTimeConversion" data-default-value="true" />
|
|
||||||
<label for="websiteAutoTimeConversion">Convert to local time</label><br />
|
|
||||||
|
|
||||||
<input type="checkbox" id="websiteShowNotificationsCountInTab" data-default-value="true" />
|
|
||||||
<label for="websiteShowNotificationsCountInTab">Show notification counter in browser tab</label><br />
|
|
||||||
|
|
||||||
<input type="checkbox" id="websiteHideUnusedTabs" data-default-value="true" />
|
|
||||||
<label for="websiteHideUnusedTabs">Hide tabs without functionality</label><br />
|
|
||||||
|
|
||||||
<input type="checkbox" id="websiteOptimizeListAppearance" data-default-value="true" />
|
|
||||||
<label for="websiteOptimizeListAppearance">Optimize appearance of lists</label><br />
|
|
||||||
|
|
||||||
<input type="checkbox" id="websiteOptimizeFontColors" data-default-value="true" />
|
|
||||||
<label for="websiteOptimizeFontColors">Optimize font colors</label><br />
|
|
||||||
|
|
||||||
<h3>Anime</h3>
|
|
||||||
<input type="checkbox" id="animeLanguageDisplay" data-default-value="true" />
|
|
||||||
<label for="animeLanguageDisplay">Optimize presentation of available subs and dubs</label><br />
|
|
||||||
|
|
||||||
<h3>Requests page</h3>
|
|
||||||
<input type="checkbox" id="requestBeautifyPage" data-default-value="true" />
|
|
||||||
<label for="requestBeautifyPage">Enhance "Requestes" page</label><br />
|
|
||||||
<h2>Player Settings</h2>
|
|
||||||
<hr />
|
|
||||||
<h3>General</h3>
|
|
||||||
<input type="checkbox" id="playerAutoplayAfterScreenshot" data-default-value="true" />
|
|
||||||
<label for="playerAutoplayAfterScreenshot">Autoplay after screenshots</label><br />
|
|
||||||
|
|
||||||
<input type="checkbox" id="playerAutopauseAfterFocusLost" data-default-value="true" />
|
|
||||||
<label for="playerAutopauseAfterFocusLost">Autopause after tab unfocused</label><br />
|
|
||||||
|
|
||||||
<input type="checkbox" id="playerAutoplayAfterFocusGain" data-default-value="true" />
|
|
||||||
<label for="playerAutoplayAfterFocusGain">Autoplay after tab focused</label><br />
|
|
||||||
|
|
||||||
<h3>Watch2gether</h3>
|
|
||||||
<input type="checkbox" id="w2gDisplayCharacterCounter" data-default-value="true" />
|
|
||||||
<label for="w2gDisplayCharacterCounter">Display character count in chat</label><br />
|
|
||||||
<input type="checkbox" id="w2gAutoscrollToUnseen" data-default-value="true" />
|
|
||||||
<label for="w2gAutoscrollToUnseen">Autoscroll to the first unseen episode</label><br />
|
|
||||||
|
|
||||||
<input type="checkbox" id="w2gAutotoggleHide" data-default-value="true" />
|
|
||||||
<label for="w2gAutotoggleHide">Autotoggle the Hide-Button for w2g</label><br />
|
|
||||||
|
|
||||||
<br /><br />
|
|
||||||
<button id="btnSave">Save</button>
|
|
||||||
<button id="btnReset">Reset</button>
|
|
||||||
<br />
|
|
||||||
<i>Please reload aniwatch website after you changed the settings.</i>
|
|
||||||
<hr />
|
|
||||||
<i id="version"></i> <i>- <a href="https://github.com/Serraniel/AniwatchPlus/releases">Changelogs</a></i>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<script src="../javascript/common.min.js"></script>
|
|
||||||
<script src="../javascript/settings.min.js"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -1,34 +0,0 @@
|
||||||
// core
|
|
||||||
import { initCore } from './utils/aniwatchCore';
|
|
||||||
// helper
|
|
||||||
import { initHelpers } from './utils/helpers';
|
|
||||||
// enhancements
|
|
||||||
import { init as anilyr } from './enhancements/anilyr';
|
|
||||||
import { init as animeRequests } from './enhancements/animeRequests';
|
|
||||||
import { init as fontColor } from './enhancements/fontColor';
|
|
||||||
import { init as languageDisplay } from './enhancements/languageDisplay';
|
|
||||||
import { init as notifications } from './enhancements/notifications';
|
|
||||||
import { init as quickSearch } from './enhancements/quickSearch';
|
|
||||||
import { init as timeConversion } from './enhancements/timeConversion';
|
|
||||||
import { init as watch2gether } from './enhancements/watch2gether';
|
|
||||||
// css
|
|
||||||
import { init as cssEnhancements } from './enhancements/cssEnhancements';
|
|
||||||
|
|
||||||
// core
|
|
||||||
initCore();
|
|
||||||
|
|
||||||
//helper
|
|
||||||
initHelpers();
|
|
||||||
|
|
||||||
// enhancements
|
|
||||||
anilyr();
|
|
||||||
animeRequests();
|
|
||||||
fontColor();
|
|
||||||
languageDisplay();
|
|
||||||
notifications();
|
|
||||||
quickSearch();
|
|
||||||
timeConversion();
|
|
||||||
watch2gether();
|
|
||||||
|
|
||||||
// css
|
|
||||||
cssEnhancements();
|
|
|
@ -1,117 +0,0 @@
|
||||||
import { assigned } from '../utils/helpers';
|
|
||||||
|
|
||||||
enum BrowserApi {
|
|
||||||
Unknown,
|
|
||||||
Chromium,
|
|
||||||
Firefox,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ConfigurationStorageBooleanCallback = (value: boolean) => void;
|
|
||||||
|
|
||||||
export interface ICustomBrowserStorageProvider {
|
|
||||||
setDataAsBoolean(key: string, value: boolean): void;
|
|
||||||
getDataAsBoolean(key: string, defaultValue: boolean, callback: ConfigurationStorageBooleanCallback): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
class StorageProviderChromium implements ICustomBrowserStorageProvider {
|
|
||||||
|
|
||||||
setDataAsBoolean(key: string, value: boolean): void {
|
|
||||||
let obj = {};
|
|
||||||
obj[key] = value;
|
|
||||||
|
|
||||||
this.getStorage().set(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
getDataAsBoolean(key: string, defaultValue: boolean, callback: ConfigurationStorageBooleanCallback): void {
|
|
||||||
this.getStorage().get(key, items => {
|
|
||||||
if (assigned(items) && assigned(items[key])) {
|
|
||||||
callback(items[key]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
callback(defaultValue);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private getStorage(): chrome.storage.StorageArea {
|
|
||||||
if (assigned(chrome.storage.sync)) {
|
|
||||||
return chrome.storage.sync;
|
|
||||||
}
|
|
||||||
|
|
||||||
return chrome.storage.local;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class StorageProviderFirefox implements ICustomBrowserStorageProvider {
|
|
||||||
|
|
||||||
setDataAsBoolean(key: string, value: boolean): void {
|
|
||||||
let obj = {};
|
|
||||||
obj[key] = value;
|
|
||||||
|
|
||||||
this.getStorage().set(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
getDataAsBoolean(key: string, defaultValue: boolean, callback: ConfigurationStorageBooleanCallback): void {
|
|
||||||
let promise = this.getStorage().get(key);
|
|
||||||
|
|
||||||
promise.then(items => {
|
|
||||||
if (assigned(items) && assigned(items[key])) {
|
|
||||||
callback(items[key] as boolean);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
callback(defaultValue);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private getStorage(): browser.storage.StorageArea {
|
|
||||||
if (assigned(browser.storage.sync)) {
|
|
||||||
return browser.storage.sync;
|
|
||||||
}
|
|
||||||
|
|
||||||
return browser.storage.local;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let __storageProvieder: ICustomBrowserStorageProvider;
|
|
||||||
|
|
||||||
function getBrowserApi(): BrowserApi {
|
|
||||||
if (typeof chrome !== 'undefined') {
|
|
||||||
if (typeof browser !== 'undefined') {
|
|
||||||
return BrowserApi.Firefox;
|
|
||||||
}
|
|
||||||
|
|
||||||
return BrowserApi.Chromium;
|
|
||||||
}
|
|
||||||
else if (typeof browser !== 'undefined') {
|
|
||||||
return BrowserApi.Firefox;
|
|
||||||
}
|
|
||||||
|
|
||||||
return BrowserApi.Unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createStorageProvider() {
|
|
||||||
|
|
||||||
let api = getBrowserApi();
|
|
||||||
|
|
||||||
// chromium
|
|
||||||
if (api === BrowserApi.Chromium) {
|
|
||||||
__storageProvieder = new StorageProviderChromium();
|
|
||||||
}
|
|
||||||
// firefox
|
|
||||||
else if (api === BrowserApi.Firefox) {
|
|
||||||
__storageProvieder = new StorageProviderFirefox();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw "Unknown browser API. Cannot create storage provider.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getGlobalStorageProvider(): ICustomBrowserStorageProvider {
|
|
||||||
if (!assigned(__storageProvieder)) {
|
|
||||||
createStorageProvider();
|
|
||||||
}
|
|
||||||
|
|
||||||
return __storageProvieder;
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
import { ConfigurationStorageBooleanCallback, getGlobalStorageProvider } from "../browserApi/storageProvider";
|
|
||||||
import { assigned } from "../utils/helpers";
|
|
||||||
|
|
||||||
// website
|
|
||||||
export const SETTINGS_websiteDisplayQuickSearch = 'websiteDisplayQuickSearch';
|
|
||||||
export const SETTINGS_websiteAutoTimeConversion = 'websiteAutoTimeConversion';
|
|
||||||
export const SETTINGS_websiteShowNotificationsCountInTab = 'websiteShowNotificationsCountInTab';
|
|
||||||
export const SETTINGS_websiteHideUnusedTabs = 'websiteHideUnusedTabs';
|
|
||||||
export const SETTINGS_websiteOptimizeListAppearance = 'websiteOptimizeListAppearance';
|
|
||||||
export const SETTINGS_websiteOptimizeFontColors = 'websiteOptimizeFontColors';
|
|
||||||
// anime
|
|
||||||
export const SETTINGS_animeLanguageDisplay = 'animeLanguageDisplay';
|
|
||||||
// requests
|
|
||||||
export const SETTINGS_requestBeautifyPage = 'requestBeautifyPage';
|
|
||||||
// player
|
|
||||||
export const SETTINGS_playerAutoplayAfterScreenshot = 'playerAutoplayAfterScreenshot';
|
|
||||||
export const SETTINGS_playerAutopauseAfterFocusLost = 'playerAutopauseAfterFocusLost';
|
|
||||||
export const SETTINGS_playerAutoplayAfterFocusGain = 'playerAutoplayAfterFocusGain';
|
|
||||||
// w2g
|
|
||||||
export const SETTINGS_w2gDisplayCharacterCounter = 'w2gDisplayCharacterCounter';
|
|
||||||
export const SETTINGS_w2gAutotoggleHide = 'w2gAutotoggleHide';
|
|
||||||
export const SETTINGS_w2gAutoscrollToUnseen = 'w2gAutoscrollToUnseen';
|
|
||||||
|
|
||||||
class Configuration {
|
|
||||||
settingsCache: Map<string, boolean>;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.settingsCache = new Map();
|
|
||||||
}
|
|
||||||
|
|
||||||
getProperty(key: string, callback: ConfigurationStorageBooleanCallback): void {
|
|
||||||
if (this.settingsCache.has(key)) {
|
|
||||||
callback(this.settingsCache.get(key));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
getGlobalStorageProvider().getDataAsBoolean(key, true, value => {
|
|
||||||
this.settingsCache.set(key, value);
|
|
||||||
callback(value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let __globalConfig: Configuration;
|
|
||||||
|
|
||||||
export function getGlobalConfiguration() {
|
|
||||||
if (!assigned(__globalConfig)) {
|
|
||||||
__globalConfig = new Configuration();
|
|
||||||
}
|
|
||||||
|
|
||||||
return __globalConfig;
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
import { getGlobalConfiguration,
|
|
||||||
SETTINGS_playerAutoplayAfterScreenshot,
|
|
||||||
SETTINGS_playerAutopauseAfterFocusLost,
|
|
||||||
SETTINGS_playerAutoplayAfterFocusGain } from '../configuration/configuration';
|
|
||||||
import * as core from '../utils/aniwatchCore';
|
|
||||||
import * as helper from '../utils/helpers';
|
|
||||||
|
|
||||||
const SCREENSHOT_TOOLTIP_ID = 'anilyr-screenshots-tooltip';
|
|
||||||
const PLAYER_ID = 'player';
|
|
||||||
let resumePlayerOnVisible: boolean;
|
|
||||||
|
|
||||||
export function init(): void {
|
|
||||||
getGlobalConfiguration().getProperty(SETTINGS_playerAutoplayAfterScreenshot, value => {
|
|
||||||
if (value) {
|
|
||||||
core.registerScript((node: Node) => {
|
|
||||||
let element = node as HTMLElement;
|
|
||||||
if (helper.assigned(element) && element.id === SCREENSHOT_TOOLTIP_ID) {
|
|
||||||
observeScreenshotTooltip(element);
|
|
||||||
}
|
|
||||||
}, "^/anime/[0-9]*/[0-9]*$");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
getGlobalConfiguration().getProperty(SETTINGS_playerAutopauseAfterFocusLost, value => {
|
|
||||||
if (value) {
|
|
||||||
core.registerScript((node: Node) => {
|
|
||||||
window.addEventListener('visibilitychange', observeTabFocus, false);
|
|
||||||
}, "^/anime/[0-9]*/[0-9]*$");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
getGlobalConfiguration().getProperty(SETTINGS_playerAutoplayAfterFocusGain, value => {
|
|
||||||
resumePlayerOnVisible = value;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function observeScreenshotTooltip(tooltip: HTMLElement): void {
|
|
||||||
let observer = new MutationObserver(mutations => {
|
|
||||||
mutations.forEach(mutation => {
|
|
||||||
// Switched to invisible
|
|
||||||
if (!mutation.oldValue.includes('display: none') && helper.isHtmlElement(mutation.target) && (mutation.target as HTMLElement).style.display == 'none') {
|
|
||||||
let playerElement = findPlayerElement(PLAYER_ID);
|
|
||||||
if (helper.assigned(playerElement)) {
|
|
||||||
resumePlayer(playerElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
observer.observe(tooltip, {
|
|
||||||
attributes: true,
|
|
||||||
attributeOldValue: true,
|
|
||||||
attributeFilter: ['style'],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function observeTabFocus(): void {
|
|
||||||
let docState = document.visibilityState;
|
|
||||||
let playerElement = findPlayerElement(PLAYER_ID);
|
|
||||||
if (docState === 'hidden') {
|
|
||||||
if (helper.assigned(playerElement)) {
|
|
||||||
pausePlayer(playerElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (docState === 'visible' && resumePlayerOnVisible) {
|
|
||||||
if (helper.assigned(playerElement)) {
|
|
||||||
resumePlayer(playerElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function findPlayerElement(id: string): HTMLVideoElement {
|
|
||||||
let playerCandidate = document.getElementById(id);
|
|
||||||
if (playerCandidate instanceof HTMLVideoElement) {
|
|
||||||
return playerCandidate;
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function resumePlayer(player: HTMLVideoElement) {
|
|
||||||
player.play();
|
|
||||||
}
|
|
||||||
|
|
||||||
function pausePlayer(player: HTMLVideoElement) {
|
|
||||||
player.pause()
|
|
||||||
}
|
|
|
@ -1,36 +1,32 @@
|
||||||
import { getGlobalConfiguration, SETTINGS_requestBeautifyPage } from '../configuration/configuration';
|
|
||||||
import * as core from '../utils/aniwatchCore';
|
import * as core from '../utils/aniwatchCore';
|
||||||
import * as color from '../utils/colors';
|
import * as color from '../utils/colors';
|
||||||
|
import * as helper from '../utils/helpers';
|
||||||
|
|
||||||
export function init(): void {
|
export function init() {
|
||||||
getGlobalConfiguration().getProperty(SETTINGS_requestBeautifyPage, value => {
|
core.registerScript(node => {
|
||||||
if (value) {
|
|
||||||
core.registerScript((node: Node) => {
|
|
||||||
// run the scripts
|
// run the scripts
|
||||||
if (node instanceof HTMLElement) {
|
if (helper.isHtmlElement(node)) {
|
||||||
changeFollowedStarColor(node);
|
changeFollowedStarColor(node);
|
||||||
changeBorderColorOwnRequests(node);
|
changeBorderColorOwnRequests(node);
|
||||||
removeUnknownUsers(node);
|
removeUnknownUsers(node);
|
||||||
}
|
}
|
||||||
}, "/requests");
|
}, "/requests");
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeFollowedStarColor(node: HTMLElement): void {
|
function changeFollowedStarColor(node) {
|
||||||
const STAR_ICON = 'star';
|
const starIcon = 'star';
|
||||||
|
|
||||||
// find stars
|
// find stars
|
||||||
let followedItems = Array.from(node.querySelectorAll('i')).filter(i => i.innerText.trim() === STAR_ICON);
|
let followedItems = Array.from(node.querySelectorAll('i')).filter(i => i.innerText.trim() === starIcon);
|
||||||
|
|
||||||
// change color
|
// change color
|
||||||
followedItems.forEach(item => item.style.color = color.aniBlue);
|
followedItems.forEach(item => item.style.color = color.aniBlue);
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeBorderColorOwnRequests(node: HTMLElement): void {
|
function changeBorderColorOwnRequests(node) {
|
||||||
const TARGET_TAG_NAME = 'MD-LIST-ITEM'; // tagName is upper case
|
const targetTagName = 'MD-LIST-ITEM'; // tagName is upper case
|
||||||
|
|
||||||
let updateFunc = (item: HTMLElement): void => {
|
let updateFunc = item => {
|
||||||
let profileLink = item.querySelectorAll('a[href*="/profile/"]:not([href="/profile/false"])');
|
let profileLink = item.querySelectorAll('a[href*="/profile/"]:not([href="/profile/false"])');
|
||||||
|
|
||||||
// highlight left border for own request
|
// highlight left border for own request
|
||||||
|
@ -40,7 +36,7 @@ function changeBorderColorOwnRequests(node: HTMLElement): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
// are we target tag?
|
// are we target tag?
|
||||||
if (node.tagName === TARGET_TAG_NAME) {
|
if (node.tagName === targetTagName) {
|
||||||
updateFunc(node);
|
updateFunc(node);
|
||||||
} else {
|
} else {
|
||||||
// find items -> all
|
// find items -> all
|
||||||
|
@ -48,17 +44,15 @@ function changeBorderColorOwnRequests(node: HTMLElement): void {
|
||||||
|
|
||||||
// update borders
|
// update borders
|
||||||
requestItems.forEach(item => {
|
requestItems.forEach(item => {
|
||||||
if (item instanceof HTMLElement) {
|
|
||||||
updateFunc(item);
|
updateFunc(item);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeUnknownUsers(node: HTMLElement): void {
|
function removeUnknownUsers(node) {
|
||||||
const TARGET_TAG_NAME = 'MD-LIST-ITEM'; // tagName is upper case
|
const targetTagName = 'MD-LIST-ITEM'; // tagName is upper case
|
||||||
|
|
||||||
let updateFunc = (item: Element) => {
|
let updateFunc = item => {
|
||||||
// find user profile link -> own request
|
// find user profile link -> own request
|
||||||
let profileLink = item.querySelectorAll('a[href*="/profile/"]:not([href="/profile/false"])');
|
let profileLink = item.querySelectorAll('a[href*="/profile/"]:not([href="/profile/false"])');
|
||||||
|
|
||||||
|
@ -67,7 +61,7 @@ function removeUnknownUsers(node: HTMLElement): void {
|
||||||
let lowerDiv = upperDiv.parentElement.nextElementSibling;
|
let lowerDiv = upperDiv.parentElement.nextElementSibling;
|
||||||
|
|
||||||
// remember Data
|
// remember Data
|
||||||
let anime = lowerDiv.textContent;
|
let anime = lowerDiv.innerText;
|
||||||
let profileData = upperDiv.innerHTML;
|
let profileData = upperDiv.innerHTML;
|
||||||
|
|
||||||
// add user note if own request
|
// add user note if own request
|
||||||
|
@ -77,7 +71,7 @@ function removeUnknownUsers(node: HTMLElement): void {
|
||||||
let parsedDocument = parser.parseFromString(profileData, 'text/html');
|
let parsedDocument = parser.parseFromString(profileData, 'text/html');
|
||||||
|
|
||||||
lowerDiv.innerHTML = '';
|
lowerDiv.innerHTML = '';
|
||||||
while (parsedDocument.body.hasChildNodes()) {
|
while(parsedDocument.body.hasChildNodes()){
|
||||||
lowerDiv.appendChild(parsedDocument.body.removeChild(parsedDocument.body.firstChild));
|
lowerDiv.appendChild(parsedDocument.body.removeChild(parsedDocument.body.firstChild));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,7 +87,7 @@ function removeUnknownUsers(node: HTMLElement): void {
|
||||||
upperDiv.appendChild(bElement);
|
upperDiv.appendChild(bElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.tagName === TARGET_TAG_NAME) {
|
if (node.tagName === targetTagName) {
|
||||||
updateFunc(node);
|
updateFunc(node);
|
||||||
} else {
|
} else {
|
||||||
// find items -> all
|
// find items -> all
|
|
@ -1,60 +0,0 @@
|
||||||
import { getGlobalConfiguration, SETTINGS_websiteHideUnusedTabs, SETTINGS_websiteOptimizeListAppearance } from '../configuration/configuration';
|
|
||||||
import * as core from '../utils/aniwatchCore';
|
|
||||||
|
|
||||||
export function init(): void {
|
|
||||||
getGlobalConfiguration().getProperty(SETTINGS_websiteHideUnusedTabs, value => {
|
|
||||||
// if disabled, add class to avoid our css optimizations
|
|
||||||
if (!value) {
|
|
||||||
let disableFunc = (node: Element) => {
|
|
||||||
let disableNode = (node: Element) => {
|
|
||||||
node.classList.add('awp-hide-unused-disabled')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.tagName === 'MD-TAB-ITEM') {
|
|
||||||
disableNode(node);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
node.querySelectorAll('md-tab-item').forEach(node => disableNode(node));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
core.registerScript((node: Node) => {
|
|
||||||
if (node instanceof Element) {
|
|
||||||
disableFunc(node);
|
|
||||||
}
|
|
||||||
}, ".*");
|
|
||||||
|
|
||||||
core.runAfterLoad(() => {
|
|
||||||
disableFunc(document.body);
|
|
||||||
}, ".*");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
getGlobalConfiguration().getProperty(SETTINGS_websiteOptimizeListAppearance, value => {
|
|
||||||
// if disabled, add class to avoid our css optimizations
|
|
||||||
if (!value) {
|
|
||||||
let disableFunc = (node: Element) => {
|
|
||||||
let disableNode = (node: Element) => {
|
|
||||||
node.classList.add('awp-list-disabled')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.tagName === 'MD-LIST-ITEM') {
|
|
||||||
disableNode(node);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
node.querySelectorAll('md-list-item').forEach(node => disableNode(node));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
core.registerScript(node => {
|
|
||||||
if (node instanceof Element) {
|
|
||||||
disableFunc(node);
|
|
||||||
}
|
|
||||||
}, ".*");
|
|
||||||
|
|
||||||
core.runAfterLoad(() => {
|
|
||||||
disableFunc(document.body);
|
|
||||||
}, ".*");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,84 +0,0 @@
|
||||||
import Color from 'color';
|
|
||||||
import { getGlobalConfiguration, SETTINGS_websiteOptimizeFontColors } from '../configuration/configuration';
|
|
||||||
import * as core from '../utils/aniwatchCore';
|
|
||||||
import * as helper from '../utils/helpers';
|
|
||||||
|
|
||||||
const BADGE_CLASS = 'label';
|
|
||||||
const DARKCOLOR_CLASS = 'awp-fontColor-dark';
|
|
||||||
const __observedBadges = [];
|
|
||||||
|
|
||||||
export function init(): void {
|
|
||||||
getGlobalConfiguration().getProperty(SETTINGS_websiteOptimizeFontColors, value => {
|
|
||||||
if (value) {
|
|
||||||
core.runAfterLoad(() => {
|
|
||||||
checkRunColorOptimization(document.documentElement);
|
|
||||||
}, ".*");
|
|
||||||
|
|
||||||
core.runAfterLocationChange(() => {
|
|
||||||
checkRunColorOptimization(document.documentElement);
|
|
||||||
}, ".*");
|
|
||||||
|
|
||||||
core.registerScript((node: Node) => {
|
|
||||||
if (node instanceof Element) {
|
|
||||||
checkRunColorOptimization(node);
|
|
||||||
}
|
|
||||||
}, ".*");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkRunColorOptimization(node: Element): void {
|
|
||||||
// run the scripts
|
|
||||||
if (node.classList.contains(BADGE_CLASS)) {
|
|
||||||
tryRegisterObserverForBadge(node);
|
|
||||||
optimizeFontColorsBadges(node);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
node.querySelectorAll(`.${BADGE_CLASS}`).forEach(element => {
|
|
||||||
tryRegisterObserverForBadge(element);
|
|
||||||
optimizeFontColorsBadges(element);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function tryRegisterObserverForBadge(badge: Node): void {
|
|
||||||
// some badges change there color via late loading so we also have to observe the classlist
|
|
||||||
// example: Navigating from a list to an anime -> "Currently Airing" late loads the color badge
|
|
||||||
// this only happens when navigating from a list, direct loading works
|
|
||||||
|
|
||||||
if (__observedBadges.indexOf(badge) >= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let obsever = new MutationObserver(mutations => {
|
|
||||||
mutations.forEach(mutation => {
|
|
||||||
// prevent recursive calls when our class is added / removed
|
|
||||||
if (mutation.target instanceof Element) {
|
|
||||||
if ((mutation.oldValue?.indexOf(DARKCOLOR_CLASS) >= 0 ?? false) !== (mutation.target?.classList?.contains(DARKCOLOR_CLASS) ?? false)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
optimizeFontColorsBadges(mutation.target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
obsever.observe(badge, {
|
|
||||||
attributes: true,
|
|
||||||
attributeFilter: ['class'],
|
|
||||||
attributeOldValue: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
__observedBadges.push(badge);
|
|
||||||
}
|
|
||||||
|
|
||||||
function optimizeFontColorsBadges(badge: Element): void {
|
|
||||||
let colorStr = window.getComputedStyle(badge, null).getPropertyValue('background-color');
|
|
||||||
|
|
||||||
// some elements do not have a computed background color
|
|
||||||
if (colorStr.length > 0) {
|
|
||||||
let color = new Color(colorStr)
|
|
||||||
badge.classList.toggle(DARKCOLOR_CLASS, color.isLight());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +1,44 @@
|
||||||
import { getGlobalConfiguration, SETTINGS_animeLanguageDisplay } from '../configuration/configuration';
|
|
||||||
import * as core from '../utils/aniwatchCore';
|
import * as core from '../utils/aniwatchCore';
|
||||||
import * as helper from '../utils/helpers';
|
import * as helper from '../utils/helpers';
|
||||||
|
|
||||||
const MANIPULATED_ATTR_NAME = 'awpManipulated';
|
export function init() {
|
||||||
|
core.registerScript(node => {
|
||||||
export function init(): void {
|
|
||||||
getGlobalConfiguration().getProperty(SETTINGS_animeLanguageDisplay, value => {
|
|
||||||
if (value) {
|
|
||||||
core.registerScript((node: Node) => {
|
|
||||||
// run the scripts
|
// run the scripts
|
||||||
if (node instanceof Element) {
|
if (helper.isHtmlElement(node)) {
|
||||||
updateLanguageDisplay(node)
|
updateLanguageDisplay(node)
|
||||||
}
|
}
|
||||||
}, "^/anime/[0-9]*$");
|
}, "^/anime/[0-9]*$");
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateLanguageDisplay(node: Element): void {
|
function updateLanguageDisplay(node) {
|
||||||
const LIST_NODE_NAME = 'MD-LIST-ITEM';
|
const listNodeName = 'MD-LIST-ITEM';
|
||||||
const BOX_NODE_NAME = 'DIV';
|
const boxNodeName = 'DIV';
|
||||||
const BOX_CLASS_NAME = 'card-margin';
|
const boxClassName = 'card-margin';
|
||||||
|
|
||||||
if (node.nodeName === LIST_NODE_NAME) {
|
if (node.nodeName === listNodeName) {
|
||||||
updateLanguageDisplayListMode(node);
|
updateLanguageDisplayListMode(node);
|
||||||
}
|
}
|
||||||
else if (node.nodeName === BOX_NODE_NAME && node.classList.contains(BOX_CLASS_NAME)) {
|
else if (node.nodeName === boxNodeName && node.classList.contains(boxClassName)) {
|
||||||
updateLanguageDisplayBoxMode(node);
|
updateLanguageDisplayBoxMode(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateLanguageDisplayListMode(node: Element): void {
|
function updateLanguageDisplayListMode(node) {
|
||||||
// last column with flags
|
// last column with flags
|
||||||
let col = node.querySelector('h3.layout-align-end-center');
|
let col = node.querySelector('h3.layout-align-end-center');
|
||||||
|
|
||||||
if (!helper.assigned(col) || col.hasAttribute(MANIPULATED_ATTR_NAME)) {
|
if (typeof col === 'undefined' || col.awpManipulated) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
doUpdateLanguageDisplay(col, false);
|
doUpdateLanguageDisplay(col, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateLanguageDisplayBoxMode(node: Element): void {
|
function updateLanguageDisplayBoxMode(node) {
|
||||||
// last column with flags
|
// last column with flags
|
||||||
let col = node.querySelector('div.layout-align-end-start');
|
let col = node.querySelector('div.layout-align-end-start');
|
||||||
|
|
||||||
if (!helper.assigned(col) || col.hasAttribute(MANIPULATED_ATTR_NAME)) {
|
if (typeof col === 'undefined' || col.awpManipulated) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,27 +46,27 @@ function updateLanguageDisplayBoxMode(node: Element): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function doUpdateLanguageDisplay(parent: Element, isBoxedModed: boolean): void {
|
function doUpdateLanguageDisplay(parent, isBoxedModed) {
|
||||||
const LIST_LANG_PREFIX = 'ep.lang.';
|
const listLangPrefix = 'ep.lang.';
|
||||||
const BOX_LANG_PREFIX = 'episodeObject.lang.';
|
const boxLangPrefix = 'episodeObject.lang.';
|
||||||
// aniwatch uses different prefixes in list und box mode :/
|
// aniwatch uses different prefixes in list und box mode :/
|
||||||
let realLangPrefix = isBoxedModed ? BOX_LANG_PREFIX : LIST_LANG_PREFIX;
|
let realLangPrefix = isBoxedModed ? boxLangPrefix : listLangPrefix;
|
||||||
|
|
||||||
const DUB_SUFFIX = 'dub';
|
const dubSuffix = 'dub';
|
||||||
const SUB_SUFFIX = 'sub';
|
const subSuffix = 'sub';
|
||||||
|
|
||||||
const DUB_ICON = 'volume_up';
|
const dubIcon = 'volume_up';
|
||||||
const SUB_ICON = 'closed_caption';
|
const subIcon = 'closed_caption';
|
||||||
const ZERO_WIDTH_SPACE_CHARACTER = ''; // ​
|
const zeroWidthSpace = ''; // ​
|
||||||
|
|
||||||
let subs: Array<string> = [];
|
let subs = [];
|
||||||
let dubs: Array<string> = [];
|
let dubs = [];
|
||||||
|
|
||||||
// find subs
|
// find subs
|
||||||
let subCols = parent.querySelectorAll('[ng-hide*="sub"]');
|
let subCols = parent.querySelectorAll('[ng-hide*="sub"]');
|
||||||
subCols.forEach((element: Element) => {
|
subCols.forEach(element => {
|
||||||
let langAttr = element.attributes['ng-hide'].value;
|
let langAttr = element.attributes['ng-hide'].value;
|
||||||
let lang = langAttr.substring(langAttr.indexOf(realLangPrefix) + realLangPrefix.length, langAttr.indexOf(SUB_SUFFIX));
|
let lang = langAttr.substring(langAttr.indexOf(realLangPrefix) + realLangPrefix.length, langAttr.indexOf(subSuffix));
|
||||||
if (element.attributes['aria-hidden'].value == 'false') {
|
if (element.attributes['aria-hidden'].value == 'false') {
|
||||||
subs.push(lang);
|
subs.push(lang);
|
||||||
}
|
}
|
||||||
|
@ -81,9 +74,9 @@ function doUpdateLanguageDisplay(parent: Element, isBoxedModed: boolean): void {
|
||||||
|
|
||||||
// find dubs
|
// find dubs
|
||||||
let dubCols = parent.querySelectorAll('[ng-hide*="dub"]');
|
let dubCols = parent.querySelectorAll('[ng-hide*="dub"]');
|
||||||
dubCols.forEach((element: Element) => {
|
dubCols.forEach(element => {
|
||||||
let langAttr = element.attributes['ng-hide'].value;
|
let langAttr = element.attributes['ng-hide'].value;
|
||||||
let lang = langAttr.substring(langAttr.indexOf(realLangPrefix) + realLangPrefix.length, langAttr.indexOf(DUB_SUFFIX));
|
let lang = langAttr.substring(langAttr.indexOf(realLangPrefix) + realLangPrefix.length, langAttr.indexOf(dubSuffix));
|
||||||
if (element.attributes['aria-hidden'].value == 'false') {
|
if (element.attributes['aria-hidden'].value == 'false') {
|
||||||
dubs.push(lang);
|
dubs.push(lang);
|
||||||
}
|
}
|
||||||
|
@ -109,12 +102,12 @@ function doUpdateLanguageDisplay(parent: Element, isBoxedModed: boolean): void {
|
||||||
let dubIconDiv = document.createElement('i');
|
let dubIconDiv = document.createElement('i');
|
||||||
if (iconsRequired) {
|
if (iconsRequired) {
|
||||||
dubIconDiv.classList.add('material-icons', 'mr-3');
|
dubIconDiv.classList.add('material-icons', 'mr-3');
|
||||||
dubIconDiv.innerText = DUB_ICON;
|
dubIconDiv.innerText = dubIcon;
|
||||||
}
|
}
|
||||||
// add dummy with 24px for correct presentation
|
// add dummy with 24px for correct presentation
|
||||||
else {
|
else {
|
||||||
dubIconDiv.style.height = '24px';
|
dubIconDiv.style.height = '24px';
|
||||||
dubIconDiv.innerText = ZERO_WIDTH_SPACE_CHARACTER;
|
dubIconDiv.innerText = zeroWidthSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
dubDiv.appendChild(dubIconDiv);
|
dubDiv.appendChild(dubIconDiv);
|
||||||
|
@ -135,16 +128,16 @@ function doUpdateLanguageDisplay(parent: Element, isBoxedModed: boolean): void {
|
||||||
let subIconDiv = document.createElement('i');
|
let subIconDiv = document.createElement('i');
|
||||||
if (iconsRequired) {
|
if (iconsRequired) {
|
||||||
subIconDiv.classList.add('material-icons', 'mr-3');
|
subIconDiv.classList.add('material-icons', 'mr-3');
|
||||||
subIconDiv.innerText = SUB_ICON;
|
subIconDiv.innerText = subIcon;
|
||||||
}
|
}
|
||||||
// add dummy with 24px for correct presentation
|
// add dummy with 24px for correct presentation
|
||||||
else {
|
else {
|
||||||
subIconDiv.style.height = '24px';
|
subIconDiv.style.height = '24px';
|
||||||
subIconDiv.innerText = ZERO_WIDTH_SPACE_CHARACTER;
|
subIconDiv.innerText = zeroWidthSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
subDiv.appendChild(subIconDiv);
|
subDiv.appendChild(subIconDiv);
|
||||||
subs.forEach((lang: string) => {
|
subs.forEach(lang => {
|
||||||
let langIcon = document.createElement('i');
|
let langIcon = document.createElement('i');
|
||||||
langIcon.classList.add('flag', `flag-${lang}`, 'mg-all-1');
|
langIcon.classList.add('flag', `flag-${lang}`, 'mg-all-1');
|
||||||
subDiv.appendChild(langIcon);
|
subDiv.appendChild(langIcon);
|
||||||
|
@ -157,7 +150,7 @@ function doUpdateLanguageDisplay(parent: Element, isBoxedModed: boolean): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dubs.length > 0) {
|
if (dubs.length > 0) {
|
||||||
dubs.forEach((lang: string) => {
|
dubs.forEach(lang => {
|
||||||
let colDiv = document.createElement('div');
|
let colDiv = document.createElement('div');
|
||||||
colDiv.setAttribute('layout', 'column');
|
colDiv.setAttribute('layout', 'column');
|
||||||
colDiv.classList.add('layout-column');
|
colDiv.classList.add('layout-column');
|
||||||
|
@ -170,12 +163,12 @@ function doUpdateLanguageDisplay(parent: Element, isBoxedModed: boolean): void {
|
||||||
let dubIconDiv = document.createElement('i');
|
let dubIconDiv = document.createElement('i');
|
||||||
if (iconsRequired) {
|
if (iconsRequired) {
|
||||||
dubIconDiv.classList.add('material-icons', 'mr-3');
|
dubIconDiv.classList.add('material-icons', 'mr-3');
|
||||||
dubIconDiv.innerText = DUB_ICON;
|
dubIconDiv.innerText = dubIcon;
|
||||||
}
|
}
|
||||||
// add dummy with 24px for correct presentation
|
// add dummy with 24px for correct presentation
|
||||||
else {
|
else {
|
||||||
dubIconDiv.style.height = '24px';
|
dubIconDiv.style.height = '24px';
|
||||||
dubIconDiv.innerText = ZERO_WIDTH_SPACE_CHARACTER;
|
dubIconDiv.innerText = zeroWidthSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
dubDiv.appendChild(dubIconDiv);
|
dubDiv.appendChild(dubIconDiv);
|
||||||
|
@ -196,12 +189,12 @@ function doUpdateLanguageDisplay(parent: Element, isBoxedModed: boolean): void {
|
||||||
let subIconDiv = document.createElement('i');
|
let subIconDiv = document.createElement('i');
|
||||||
if (iconsRequired) {
|
if (iconsRequired) {
|
||||||
subIconDiv.classList.add('material-icons', 'mr-3');
|
subIconDiv.classList.add('material-icons', 'mr-3');
|
||||||
subIconDiv.innerText = SUB_ICON;
|
subIconDiv.innerText = subIcon;
|
||||||
}
|
}
|
||||||
// add dummy with 24px for correct presentation
|
// add dummy with 24px for correct presentation
|
||||||
else {
|
else {
|
||||||
subIconDiv.style.height = '24px';
|
subIconDiv.style.height = '24px';
|
||||||
subIconDiv.innerText = ZERO_WIDTH_SPACE_CHARACTER;
|
subIconDiv.innerText = zeroWidthSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
subDiv.appendChild(subIconDiv);
|
subDiv.appendChild(subIconDiv);
|
||||||
|
@ -219,17 +212,13 @@ function doUpdateLanguageDisplay(parent: Element, isBoxedModed: boolean): void {
|
||||||
});
|
});
|
||||||
|
|
||||||
parent.querySelectorAll('.layout-column:not(:last-child)').forEach(div => {
|
parent.querySelectorAll('.layout-column:not(:last-child)').forEach(div => {
|
||||||
if (div instanceof HTMLElement) {
|
|
||||||
div.style.borderRight = '1px solid rgba(155,155,155, 0.2)';
|
div.style.borderRight = '1px solid rgba(155,155,155, 0.2)';
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
parent.querySelectorAll('.layout-column').forEach(div => {
|
parent.querySelectorAll('.layout-column').forEach(div => {
|
||||||
if (div instanceof HTMLElement) {
|
|
||||||
div.style.paddingLeft = '2px';
|
div.style.paddingLeft = '2px';
|
||||||
div.style.paddingRight = '2px';
|
div.style.paddingRight = '2px';
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
parent.setAttribute('awpManipulated', String(true));
|
parent.awpManipulated = true;
|
||||||
}
|
}
|
|
@ -1,38 +0,0 @@
|
||||||
import { getGlobalConfiguration, SETTINGS_websiteShowNotificationsCountInTab } from '../configuration/configuration';
|
|
||||||
import * as core from '../utils/aniwatchCore';
|
|
||||||
import * as helper from '../utils/helpers';
|
|
||||||
|
|
||||||
export function init(): void {
|
|
||||||
getGlobalConfiguration().getProperty(SETTINGS_websiteShowNotificationsCountInTab, value => {
|
|
||||||
if (value) {
|
|
||||||
core.runAfterLoad(() => {
|
|
||||||
updateNotificationsInTitle();
|
|
||||||
}, ".*");
|
|
||||||
|
|
||||||
core.runAfterLocationChange(() => {
|
|
||||||
updateNotificationsInTitle();
|
|
||||||
}, ".*");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getNotificationCount(): number {
|
|
||||||
if (core.isLoggedIn()) {
|
|
||||||
let menuUserText = document.getElementById('materialize-menu-dropdown').innerText.split('\n')[4];
|
|
||||||
let notificationCount = parseInt(menuUserText.match(/\d+/)?.[0]) ?? 0;
|
|
||||||
return notificationCount;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateNotificationsInTitle(): void {
|
|
||||||
let count = getNotificationCount();
|
|
||||||
|
|
||||||
if (helper.assigned(count) && count > 0) {
|
|
||||||
// document.title is updated after the event is triggered, so we delay our title update by a reasonable time
|
|
||||||
setTimeout(() => {
|
|
||||||
document.title = `(${count}) ${document.title}`;
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +1,16 @@
|
||||||
import { getGlobalConfiguration, SETTINGS_websiteDisplayQuickSearch } from '../configuration/configuration';
|
|
||||||
import * as core from '../utils/aniwatchCore';
|
import * as core from '../utils/aniwatchCore';
|
||||||
import * as helper from '../utils/helpers';
|
import * as helper from '../utils/helpers';
|
||||||
|
|
||||||
const quickSearchID = 'ea-quickSearch';
|
const quickSearchID = 'ea-quickSearch';
|
||||||
const quickSearchLink = 'ea-quickSearchLink';
|
const quickSearchLink = 'ea-quickSearchLink';
|
||||||
|
|
||||||
export function init(): void {
|
export function init() {
|
||||||
getGlobalConfiguration().getProperty(SETTINGS_websiteDisplayQuickSearch, value => {
|
|
||||||
if (value) {
|
|
||||||
core.runAfterLoad(() => {
|
core.runAfterLoad(() => {
|
||||||
initSearch();
|
initSearch();
|
||||||
}, ".*");
|
}, ".*");
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function initSearch(): void {
|
function initSearch() {
|
||||||
let entry = document.createElement('li');
|
let entry = document.createElement('li');
|
||||||
entry.setAttribute('ng-repeat', 'item in navbar');
|
entry.setAttribute('ng-repeat', 'item in navbar');
|
||||||
entry.setAttribute('ng-class', '{\'anime-indicator\': item[\'@attributes\'].title==\'Anime\'}');
|
entry.setAttribute('ng-class', '{\'anime-indicator\': item[\'@attributes\'].title==\'Anime\'}');
|
||||||
|
@ -45,13 +40,13 @@ function initSearch(): void {
|
||||||
document.addEventListener('keypress', event => handleSearchForShiftF(event));
|
document.addEventListener('keypress', event => handleSearchForShiftF(event));
|
||||||
|
|
||||||
// additionally, the last dropdown ul has a "right: 0px" style, which has to be fixed with auto, otherwhise it will pop up in the wrong position
|
// additionally, the last dropdown ul has a "right: 0px" style, which has to be fixed with auto, otherwhise it will pop up in the wrong position
|
||||||
(Array.from(menu.querySelectorAll('ul.dropdown')).slice(-1)[0] as HTMLElement).style.right = 'auto';
|
Array.from(menu.querySelectorAll('ul.dropdown')).slice(-1)[0].style.right = 'auto';
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleQuickSearch(event: KeyboardEvent): void {
|
function handleQuickSearch(event) {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
let quickSearchElement = document.getElementById(quickSearchID) as HTMLInputElement;
|
let quickSearchElement = document.getElementById(quickSearchID);
|
||||||
let linkElement = document.getElementById(quickSearchLink) as HTMLAnchorElement;
|
let linkElement = document.getElementById(quickSearchLink);
|
||||||
|
|
||||||
let url = new URL(window.location.origin)
|
let url = new URL(window.location.origin)
|
||||||
url.pathname = '/search';
|
url.pathname = '/search';
|
||||||
|
@ -67,10 +62,10 @@ function handleQuickSearch(event: KeyboardEvent): void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSearchForShiftF(event: KeyboardEvent): void {
|
function handleSearchForShiftF(event) {
|
||||||
if (helper.isShiftPressed) {
|
if (helper.isShiftPressed) {
|
||||||
// check if some kind of input is focused already; we then prevent our hotkey
|
// check if some kind of input is focused already; we then prevent our hotkey
|
||||||
if (document.activeElement instanceof HTMLInputElement || document.activeElement instanceof HTMLTextAreaElement || ((document.activeElement as HTMLElement)?.isContentEditable ?? false)) {
|
if (document.activeElement instanceof HTMLInputElement || document.activeElement.isContentEditable) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,228 +0,0 @@
|
||||||
import spacetime from 'spacetime';
|
|
||||||
import { getGlobalConfiguration, SETTINGS_websiteAutoTimeConversion } from '../configuration/configuration';
|
|
||||||
import * as core from '../utils/aniwatchCore';
|
|
||||||
import * as helper from '../utils/helpers';
|
|
||||||
|
|
||||||
const __alteredNodes = [];
|
|
||||||
const DAYS = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
|
|
||||||
|
|
||||||
export function init(): void {
|
|
||||||
getGlobalConfiguration().getProperty(SETTINGS_websiteAutoTimeConversion, value => {
|
|
||||||
if (value) {
|
|
||||||
// The regexp pattern matches anything except the airing page.
|
|
||||||
// This is because we would have to restructure the complete site to update time data.
|
|
||||||
// Additionally, there is a big hint that all data would be UTC+1
|
|
||||||
core.runAfterLoad(() => {
|
|
||||||
updateTimestamps(document.documentElement);
|
|
||||||
}, "^/(?!airing).*$");
|
|
||||||
|
|
||||||
core.runAfterLocationChange(() => {
|
|
||||||
updateTimestamps(document.documentElement);
|
|
||||||
}, "^/(?!airing).*$");
|
|
||||||
|
|
||||||
core.registerScript((node: Node) => {
|
|
||||||
updateTimestamps(node);
|
|
||||||
}, "^/(?!airing).*$");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSpaceDateTimeFormat(use24Format: boolean): string {
|
|
||||||
return `${getSpaceDateFormat()} ${getSpaceTimeFormat(use24Format)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSpaceTimeFormat(use24Format: boolean): string {
|
|
||||||
if (use24Format) {
|
|
||||||
return '{time-24}';
|
|
||||||
}
|
|
||||||
|
|
||||||
return '{time}';
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSpaceDateFormat(): string {
|
|
||||||
return '{date}. {month-short} {year}';
|
|
||||||
}
|
|
||||||
|
|
||||||
function tryUpdateDateTime(node: Node): boolean {
|
|
||||||
const REG_DATETIME = /(\d{2}(\/|\.)){2}\d{4} *\d?\d:\d{2}( (AM|PM))?/g;
|
|
||||||
const REG_TIME = /\d?\d:\d{2}/;
|
|
||||||
const REG_AMPM = /\s(am|pm)/i;
|
|
||||||
|
|
||||||
let hits = Array.from(node.textContent.matchAll(REG_DATETIME), match => match[0]);
|
|
||||||
|
|
||||||
if (hits.length === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
hits.forEach(hit => {
|
|
||||||
let use24Format = false;
|
|
||||||
let processedStr = hit
|
|
||||||
|
|
||||||
// string must be converted into 12h format
|
|
||||||
if (processedStr.search(REG_AMPM) < 0) {
|
|
||||||
let timeStr = processedStr.match(REG_TIME)[0];
|
|
||||||
let hm = timeStr.split(':');
|
|
||||||
let hour = parseInt(hm[0]);
|
|
||||||
|
|
||||||
if (hour >= 12) {
|
|
||||||
timeStr = timeStr.replace(`${hour}:`, `${hour - 12}:`);
|
|
||||||
timeStr += 'pm';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
timeStr += 'am';
|
|
||||||
}
|
|
||||||
|
|
||||||
processedStr = processedStr.replace(REG_TIME, timeStr);
|
|
||||||
use24Format = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if time has a space before am/pm, this has to be removed for spacetime
|
|
||||||
processedStr = processedStr.replace(REG_AMPM, '$1');
|
|
||||||
|
|
||||||
let datetime = spacetime(processedStr, 'UTC+1', { dmy: true });
|
|
||||||
datetime = datetime.goto(spacetime().tz);
|
|
||||||
let replaceText = String(datetime.format(getSpaceDateTimeFormat(use24Format)));
|
|
||||||
|
|
||||||
node.textContent = node.textContent.replace(hit, replaceText);
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function tryUpdateDate(node: Node): boolean {
|
|
||||||
const REG_DATE = /(\d{2}(\/|\.)){2}\d{4}/g;
|
|
||||||
|
|
||||||
let hits = Array.from(node.textContent.matchAll(REG_DATE), match => match[0]);
|
|
||||||
|
|
||||||
if (hits.length === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
hits.forEach(hit => {
|
|
||||||
let datetime = spacetime(hit, 'UTC+1', { dmy: true });
|
|
||||||
datetime = datetime.goto(spacetime().tz);
|
|
||||||
let replaceText = String(datetime.format(getSpaceDateFormat()));
|
|
||||||
|
|
||||||
node.textContent = node.textContent.replace(hit, replaceText);
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function tryUpdateTime(node: Node): boolean {
|
|
||||||
const REG_TIME = /\d?\d:\d{2}( (AM|PM))?/g;
|
|
||||||
const REG_AMPM = /\s(am|pm)/i;
|
|
||||||
|
|
||||||
let hits = Array.from(node.textContent.matchAll(REG_TIME), match => match[0]);
|
|
||||||
|
|
||||||
if (hits.length === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
hits.forEach(hit => {
|
|
||||||
let use24Format = false;
|
|
||||||
let processedStr = hit
|
|
||||||
|
|
||||||
// string must be converted into 12h format
|
|
||||||
if (processedStr.search(REG_AMPM) < 0) {
|
|
||||||
let timeStr = processedStr.match(REG_TIME)[0];
|
|
||||||
let hm = timeStr.split(':');
|
|
||||||
let hour = parseInt(hm[0]);
|
|
||||||
|
|
||||||
if (hour >= 12) {
|
|
||||||
timeStr = timeStr.replace(`${hour}:`, `${hour - 12}:`);
|
|
||||||
timeStr += 'pm';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
timeStr += 'am';
|
|
||||||
}
|
|
||||||
|
|
||||||
processedStr = processedStr.replace(REG_TIME, timeStr);
|
|
||||||
use24Format = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if time has a space before am/pm, this has to be removed for spacetime
|
|
||||||
processedStr = processedStr.replace(REG_AMPM, '$1');
|
|
||||||
|
|
||||||
let datetime = spacetime();
|
|
||||||
datetime = datetime.goto('UTC+1');
|
|
||||||
datetime = datetime.time(processedStr);
|
|
||||||
datetime = datetime.goto(spacetime().tz);
|
|
||||||
let replaceText = String(datetime.format(getSpaceTimeFormat(use24Format)));
|
|
||||||
|
|
||||||
node.textContent = node.textContent.replace(hit, replaceText);
|
|
||||||
|
|
||||||
// SPECIAL CASE: Anime has the day written in broadcast bade. This may be different in another timezone
|
|
||||||
let tzMeta = spacetime().timezone();
|
|
||||||
let originalH = datetime.hour() - tzMeta.current.offset + 1;
|
|
||||||
|
|
||||||
let dOffset = 0;
|
|
||||||
// we moved to next day
|
|
||||||
if (originalH < 0) {
|
|
||||||
dOffset = 1;
|
|
||||||
}
|
|
||||||
// we moved to previous day
|
|
||||||
else if (originalH > 24) {
|
|
||||||
dOffset = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if day changed
|
|
||||||
if (dOffset != 0) {
|
|
||||||
let dayNode = (node.parentNode as Element)?.previousElementSibling;
|
|
||||||
if (helper.assigned(dayNode)) {
|
|
||||||
for (let i = 0; i < DAYS.length; i++) {
|
|
||||||
if (dayNode.textContent.indexOf(DAYS[i]) >= 0) {
|
|
||||||
dayNode.textContent = dayNode.textContent.replace(DAYS[i], DAYS[(i + DAYS.length + dOffset) % DAYS.length]); // add DAYS.length to avoid negative numbers in the modulo operation
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function tryUpdateTimeZone(node: Node): boolean {
|
|
||||||
const HINT_UTC = 'UTC+1';
|
|
||||||
if (node.textContent === HINT_UTC) {
|
|
||||||
let tzMeta = spacetime().timezone();
|
|
||||||
|
|
||||||
node.textContent = `${tzMeta.name} (UTC${tzMeta.current.offset >= 0 ? '+' : ''}${tzMeta.current.offset})`;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateTimestamps(node) {
|
|
||||||
let nodes = helper.findTextNodes(node);
|
|
||||||
|
|
||||||
nodes.forEach(node => {
|
|
||||||
// avoid double updates
|
|
||||||
if (__alteredNodes.indexOf(node) >= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tryUpdateDateTime(node)) {
|
|
||||||
__alteredNodes.push(node);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tryUpdateDate(node)) {
|
|
||||||
__alteredNodes.push(node);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tryUpdateTime(node)) {
|
|
||||||
__alteredNodes.push(node);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tryUpdateTimeZone(node)) {
|
|
||||||
__alteredNodes.push(node);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,153 +0,0 @@
|
||||||
import * as core from '../utils/aniwatchCore';
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
import { getGlobalConfiguration, SETTINGS_w2gDisplayCharacterCounter, SETTINGS_w2gAutotoggleHide, SETTINGS_w2gAutoscrollToUnseen } from '../configuration/configuration';
|
|
||||||
import { assigned } from '../utils/helpers';
|
|
||||||
import { findPlayerElement } from "../enhancements/anilyr";
|
|
||||||
|
|
||||||
const PLAYER_ID = 'wPlayer';
|
|
||||||
let hidden: boolean;
|
|
||||||
|
|
||||||
export function init(): void {
|
|
||||||
getGlobalConfiguration().getProperty(SETTINGS_w2gDisplayCharacterCounter, value => {
|
|
||||||
if (value) {
|
|
||||||
core.runAfterLoad(() => {
|
|
||||||
manipulateChatInput();
|
|
||||||
}, "^/watch2gether/.*$");
|
|
||||||
|
|
||||||
core.runAfterLocationChange(() => {
|
|
||||||
manipulateChatInput();
|
|
||||||
}, "^/watch2gether/.*$");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
getGlobalConfiguration().getProperty(SETTINGS_w2gAutotoggleHide, value => {
|
|
||||||
if (value) {
|
|
||||||
core.runAfterLoad(() => {
|
|
||||||
addAutohideListener();
|
|
||||||
}, "^/watch2gether/.*$");
|
|
||||||
|
|
||||||
core.runAfterLocationChange(() => {
|
|
||||||
addAutohideListener();
|
|
||||||
}, "^/watch2gether/.*$");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
getGlobalConfiguration().getProperty(SETTINGS_w2gAutoscrollToUnseen, value => {
|
|
||||||
if (value) {
|
|
||||||
core.runAfterLoad(() => {
|
|
||||||
let element = findSearchResults();
|
|
||||||
if (assigned(element)) {
|
|
||||||
scrollSearchResults(element);
|
|
||||||
}
|
|
||||||
}, "^/watch2gether/.*$");
|
|
||||||
|
|
||||||
core.runAfterLocationChange(() => {
|
|
||||||
let element = findSearchResults();
|
|
||||||
if (assigned(element)) {
|
|
||||||
scrollSearchResults(element);
|
|
||||||
}
|
|
||||||
}, "^/watch2gether/.*$");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function manipulateChatInput(): void {
|
|
||||||
let textarea = document.querySelector('.chat-input textarea') as HTMLTextAreaElement;
|
|
||||||
|
|
||||||
// avoid duplicate registration
|
|
||||||
if (assigned(textarea.dataset.charCounterId)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
addCharCounter(textarea);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addCharCounter(textarea: HTMLTextAreaElement): void {
|
|
||||||
let chatDiv = textarea.parentElement.parentElement; // div with chat input and controls
|
|
||||||
let controlRow = chatDiv.children[1]; // row with controls
|
|
||||||
let btn = controlRow.querySelector('button'); // find send button
|
|
||||||
|
|
||||||
let charCounterSpan = document.createElement('span'); // create span for counter
|
|
||||||
charCounterSpan.classList.add('awp-w2g-chatCounter');
|
|
||||||
|
|
||||||
// id and "connection"
|
|
||||||
let counterId = `awp-${uuidv4()}`
|
|
||||||
charCounterSpan.id = counterId;
|
|
||||||
textarea.dataset.charCounterId = counterId;
|
|
||||||
|
|
||||||
btn.parentElement.insertBefore(charCounterSpan, btn); // and insert in front of the button
|
|
||||||
updateCharCounter(textarea, charCounterSpan);
|
|
||||||
|
|
||||||
textarea.addEventListener('keyup', () => {
|
|
||||||
updateCharCounter(textarea, charCounterSpan)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateCharCounter(textarea: HTMLTextAreaElement, charCounterSpan: HTMLSpanElement): void {
|
|
||||||
const SHAKE_CLASS = 'awp-w2g-chatCounter-max';
|
|
||||||
|
|
||||||
let current = textarea.value.length;
|
|
||||||
let max = textarea.maxLength;
|
|
||||||
|
|
||||||
charCounterSpan.innerText = `${current} / ${max}`;
|
|
||||||
|
|
||||||
// animation if at max
|
|
||||||
if (current >= max && !charCounterSpan.classList.contains(SHAKE_CLASS)) {
|
|
||||||
charCounterSpan.classList.add(SHAKE_CLASS);
|
|
||||||
|
|
||||||
// remove css class after animation finished, so it can be restarted again
|
|
||||||
setTimeout(() => {
|
|
||||||
charCounterSpan.classList.remove(SHAKE_CLASS);
|
|
||||||
}, 200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addAutohideListener(): void {
|
|
||||||
let playerElement = findPlayerElement(PLAYER_ID);
|
|
||||||
let hideButton: HTMLButtonElement = document.getElementsByClassName('no-margin md-button md-ink-ripple layout-align-center-center layout-row')[0] as HTMLButtonElement;
|
|
||||||
if (assigned(playerElement) && assigned(hideButton)) {
|
|
||||||
if (hideButton.textContent.includes('HIDE')) {
|
|
||||||
hidden = false;
|
|
||||||
} else if (hideButton.textContent.includes('SHOW')) {
|
|
||||||
hidden = true;
|
|
||||||
}
|
|
||||||
playerElement.addEventListener('play', fn => {
|
|
||||||
if (!hidden) {
|
|
||||||
hideButton.click();
|
|
||||||
hidden = !hidden;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
playerElement.addEventListener('pause', fn => {
|
|
||||||
if (hidden) {
|
|
||||||
hideButton.click();
|
|
||||||
hidden = !hidden;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function scrollSearchResults(searchRes: Element): void {
|
|
||||||
let observer = new MutationObserver(mutations => {
|
|
||||||
let scrollTarget = searchRes.querySelector('md-list-item:not(.animelist-completed):not(.animelist-completed-add)') as HTMLElement;
|
|
||||||
|
|
||||||
if (assigned(scrollTarget)) {
|
|
||||||
// The node isn´t in its correct position directly when it´s added so we wait a small bit of time before we start scrolling.
|
|
||||||
// Also works for long loading lists which need more time to load than we wait (for example One Piece).
|
|
||||||
window.setTimeout(() => {
|
|
||||||
// scroll container to episode first
|
|
||||||
searchRes.scrollTop = scrollTarget.offsetTop;
|
|
||||||
|
|
||||||
// then scroll page to episode if neccessarry
|
|
||||||
scrollTarget.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
observer.observe(searchRes, {
|
|
||||||
childList: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function findSearchResults(): Element {
|
|
||||||
return document.querySelector('.search-results .ep-view');
|
|
||||||
}
|
|
19
src/javascript/index.js
Normal file
19
src/javascript/index.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
// core
|
||||||
|
import { initCore } from './utils/aniwatchCore';
|
||||||
|
// helper
|
||||||
|
import { initHelpers } from './utils/helpers';
|
||||||
|
// enhancements
|
||||||
|
import { init as animeRequests } from './enhancements/animeRequests';
|
||||||
|
import { init as languageDisplay } from './enhancements/languageDisplay';
|
||||||
|
import { init as quickSearch } from './enhancements/quickSearch';
|
||||||
|
|
||||||
|
// core
|
||||||
|
initCore();
|
||||||
|
|
||||||
|
//helper
|
||||||
|
initHelpers();
|
||||||
|
|
||||||
|
// enhancements
|
||||||
|
animeRequests();
|
||||||
|
languageDisplay();
|
||||||
|
quickSearch();
|
|
@ -1,51 +0,0 @@
|
||||||
import { getGlobalStorageProvider } from "./browserApi/storageProvider";
|
|
||||||
import { onReady } from "./utils/helpers";
|
|
||||||
|
|
||||||
const OPTION_SELECTOR = 'input[type="checkbox"';
|
|
||||||
|
|
||||||
function storeOptions() {
|
|
||||||
document.querySelectorAll(OPTION_SELECTOR).forEach(optionElement => {
|
|
||||||
let optionInputElement = optionElement as HTMLInputElement;
|
|
||||||
getGlobalStorageProvider().setDataAsBoolean(optionInputElement.id, optionInputElement.checked);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function restoreOptions() {
|
|
||||||
document.querySelectorAll(OPTION_SELECTOR).forEach(optionElement => {
|
|
||||||
let optionInputElement = optionElement as HTMLInputElement;
|
|
||||||
let defaultValue = optionInputElement.dataset.defaultValue === 'true' ? true : false;
|
|
||||||
|
|
||||||
getGlobalStorageProvider().getDataAsBoolean(optionInputElement.id, defaultValue, value => {
|
|
||||||
optionInputElement.checked = value;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetOptions() {
|
|
||||||
document.querySelectorAll(OPTION_SELECTOR).forEach(optionElement => {
|
|
||||||
let optionInputElement = optionElement as HTMLInputElement;
|
|
||||||
let defaultValue = optionInputElement.dataset.defaultValue === 'true' ? true : false;
|
|
||||||
|
|
||||||
optionInputElement.checked = defaultValue;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onReady(() => {
|
|
||||||
// register Store Button
|
|
||||||
document.getElementById('btnSave').addEventListener('click', event => {
|
|
||||||
event.preventDefault();
|
|
||||||
storeOptions();
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('btnReset').addEventListener('click', event => {
|
|
||||||
event.preventDefault();
|
|
||||||
resetOptions();
|
|
||||||
storeOptions();
|
|
||||||
})
|
|
||||||
|
|
||||||
// try restore options
|
|
||||||
restoreOptions();
|
|
||||||
|
|
||||||
// update version label
|
|
||||||
document.getElementById('version').innerText = `v${chrome.runtime.getManifest().version}`
|
|
||||||
});
|
|
72
src/javascript/utils/aniwatchCore.js
Normal file
72
src/javascript/utils/aniwatchCore.js
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import * as helper from './helpers';
|
||||||
|
|
||||||
|
let __scripts = [];
|
||||||
|
let __afterLoadScripts = [];
|
||||||
|
|
||||||
|
export function initCore() {
|
||||||
|
let observer = new MutationObserver(mutations => {
|
||||||
|
mutations.forEach(mutation => {
|
||||||
|
for (let i = 0; i < mutation.addedNodes.length; i++) {
|
||||||
|
runScripts(mutation.addedNodes[i]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.documentElement || document.body, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
attributes: true
|
||||||
|
});
|
||||||
|
|
||||||
|
helper.onReady(() => awaitPageLoaded());
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerScript(func, pattern = '.*') {
|
||||||
|
__scripts.push({ "function": func, "pattern": pattern });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function runScripts(node) {
|
||||||
|
__scripts.forEach(script => {
|
||||||
|
if (window.location.pathname.match(script.pattern)) {
|
||||||
|
script.function(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function findPreloader() {
|
||||||
|
return document.getElementById('preloader');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function runAfterLoad(func, pattern = '.*') {
|
||||||
|
let preloader = findPreloader();
|
||||||
|
if (typeof preloader !== undefined && preloader.style.display !== "none") {
|
||||||
|
__afterLoadScripts.push({ "function": func, "pattern": pattern });
|
||||||
|
} else {
|
||||||
|
func();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function awaitPageLoaded() {
|
||||||
|
let preLoader = findPreloader();
|
||||||
|
|
||||||
|
let runScripts = () => {
|
||||||
|
__afterLoadScripts.forEach(script => {
|
||||||
|
if (window.location.pathname.match(script.pattern)) {
|
||||||
|
script.function();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof preLoader === 'undefined') {
|
||||||
|
runScripts();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let loop = setInterval(() => {
|
||||||
|
if (preLoader.style.display === "none") {
|
||||||
|
clearInterval(loop);
|
||||||
|
|
||||||
|
runScripts();
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
|
@ -1,130 +0,0 @@
|
||||||
import * as helper from './helpers';
|
|
||||||
|
|
||||||
type ScriptCallback = () => void;
|
|
||||||
type NodeScriptCallback = (node: Node) => void;
|
|
||||||
|
|
||||||
type ScriptObj = {
|
|
||||||
function: ScriptCallback,
|
|
||||||
pattern: string
|
|
||||||
}
|
|
||||||
|
|
||||||
type NodeScriptObj = {
|
|
||||||
function: NodeScriptCallback,
|
|
||||||
pattern: string
|
|
||||||
}
|
|
||||||
|
|
||||||
/* SCRIPT LOGICS */
|
|
||||||
let __scripts: Array<NodeScriptObj> = [];
|
|
||||||
let __afterLoadScripts: Array<ScriptObj> = [];
|
|
||||||
let __afterLocationChangeScripts: Array<ScriptObj> = [];
|
|
||||||
|
|
||||||
export function initCore(): void {
|
|
||||||
let observer = new MutationObserver(mutations => {
|
|
||||||
mutations.forEach(mutation => {
|
|
||||||
for (let i = 0; i < mutation.addedNodes.length; i++) {
|
|
||||||
runScripts(mutation.addedNodes[i]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
observer.observe(document.documentElement || document.body, {
|
|
||||||
childList: true,
|
|
||||||
subtree: true,
|
|
||||||
attributes: true
|
|
||||||
});
|
|
||||||
|
|
||||||
runAfterLoad(() => {
|
|
||||||
let loadingBar = document.getElementById('enable-ani-cm');
|
|
||||||
let loadingBarObserver = new MutationObserver(mutations => {
|
|
||||||
mutations.forEach(mutation => {
|
|
||||||
// enable-ani-cm node changes from display:none to display:block after loading
|
|
||||||
if (mutation.oldValue.includes('display: none')) {
|
|
||||||
__afterLocationChangeScripts.forEach(script => {
|
|
||||||
if (window.location.pathname.match(script.pattern)) {
|
|
||||||
script.function();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
loadingBarObserver.observe(loadingBar, {
|
|
||||||
attributes: true,
|
|
||||||
attributeOldValue: true,
|
|
||||||
attributeFilter: ['style'],
|
|
||||||
});
|
|
||||||
|
|
||||||
}, '.*')
|
|
||||||
|
|
||||||
helper.onReady(() => awaitPageLoaded());
|
|
||||||
}
|
|
||||||
|
|
||||||
export function registerScript(func: NodeScriptCallback, pattern: string = '.*'): void {
|
|
||||||
__scripts.push({ function: func, pattern: pattern });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function runScripts(node: Node): void {
|
|
||||||
__scripts.forEach(script => {
|
|
||||||
if (window.location.pathname.match(script.pattern)) {
|
|
||||||
script.function(node);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function findPreloader(): HTMLElement {
|
|
||||||
return document.getElementById('preloader');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function runAfterLoad(func: ScriptCallback, pattern: string = '.*'): void {
|
|
||||||
let preloader = findPreloader();
|
|
||||||
if (typeof preloader !== undefined && preloader.style.display !== "none") {
|
|
||||||
__afterLoadScripts.push({ function: func, pattern: pattern });
|
|
||||||
} else {
|
|
||||||
func();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function awaitPageLoaded(): void {
|
|
||||||
let preLoader = findPreloader();
|
|
||||||
|
|
||||||
let runScripts = () => {
|
|
||||||
__afterLoadScripts.forEach(script => {
|
|
||||||
if (window.location.pathname.match(script.pattern)) {
|
|
||||||
script.function();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!helper.assigned(preLoader)) {
|
|
||||||
runScripts();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let loop = window.setInterval(() => {
|
|
||||||
if (preLoader.style.display === "none" && document.readyState === 'complete') {
|
|
||||||
window.clearInterval(loop);
|
|
||||||
|
|
||||||
runScripts();
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* PATHNAME LOGIC */
|
|
||||||
export function runAfterLocationChange(func: ScriptCallback, pattern: string = '.*'): void {
|
|
||||||
__afterLocationChangeScripts.push({ function: func, pattern: pattern });
|
|
||||||
}
|
|
||||||
|
|
||||||
/* LOGIN LOGIC */
|
|
||||||
export function isLoggedIn(): boolean {
|
|
||||||
let menu = document.getElementById('materialize-menu-dropdown');
|
|
||||||
let result = true;
|
|
||||||
|
|
||||||
menu.innerText.split('\n').forEach(item => {
|
|
||||||
if (item === 'Login') {
|
|
||||||
result = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
35
src/javascript/utils/helpers.js
Normal file
35
src/javascript/utils/helpers.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
export var isShiftPressed = false;
|
||||||
|
export var isCtrlPressed = false;
|
||||||
|
|
||||||
|
export function isHtmlElement(object) {
|
||||||
|
return object instanceof HTMLElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initHelpers() {
|
||||||
|
document.addEventListener('keydown', event => handleKeyDown(event));
|
||||||
|
document.addEventListener('keyup', event => handleKeyUp(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function onReady(fn) {
|
||||||
|
if (document.readyState != 'loading') {
|
||||||
|
fn();
|
||||||
|
} else {
|
||||||
|
document.addEventListener('DOMContentLoaded', fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleKeyDown(event) {
|
||||||
|
handleKeyToggle(event, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleKeyUp(event) {
|
||||||
|
handleKeyToggle(event, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleKeyToggle(event, isPressed) {
|
||||||
|
if (event.key === 'Shift') {
|
||||||
|
isShiftPressed = isPressed;
|
||||||
|
} else if (event.key === 'Control') {
|
||||||
|
isCtrlPressed = isPressed;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,54 +0,0 @@
|
||||||
export var isShiftPressed: boolean = false;
|
|
||||||
export var isCtrlPressed: boolean = false;
|
|
||||||
|
|
||||||
export function isHtmlElement(object: any) {
|
|
||||||
return object instanceof HTMLElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function initHelpers(): void {
|
|
||||||
document.addEventListener('keydown', event => handleKeyDown(event));
|
|
||||||
document.addEventListener('keyup', event => handleKeyUp(event));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function onReady(fn: () => void) {
|
|
||||||
if (document.readyState != 'loading') {
|
|
||||||
fn();
|
|
||||||
} else {
|
|
||||||
document.addEventListener('DOMContentLoaded', fn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function assigned(obj: any): boolean {
|
|
||||||
return !(typeof obj === 'undefined' || obj === null);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleKeyDown(event: KeyboardEvent) {
|
|
||||||
handleKeyToggle(event, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleKeyUp(event: KeyboardEvent) {
|
|
||||||
handleKeyToggle(event, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleKeyToggle(event: KeyboardEvent, isPressed: boolean) {
|
|
||||||
if (event.key === 'Shift') {
|
|
||||||
isShiftPressed = isPressed;
|
|
||||||
} else if (event.key === 'Control') {
|
|
||||||
isCtrlPressed = isPressed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function findTextNodes(baseNode: Node): Array<Node> {
|
|
||||||
if (!assigned(baseNode)) {
|
|
||||||
baseNode = document.documentElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
let walker = document.createTreeWalker(baseNode, NodeFilter.SHOW_TEXT, null);
|
|
||||||
let node;
|
|
||||||
let results = [];
|
|
||||||
while (node = walker.nextNode()) {
|
|
||||||
results.push(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
|
@ -5,7 +5,6 @@
|
||||||
"version_name": "$semanticVersion",
|
"version_name": "$semanticVersion",
|
||||||
"description": "$description",
|
"description": "$description",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"storage",
|
|
||||||
"*://aniwatch.me/*"
|
"*://aniwatch.me/*"
|
||||||
],
|
],
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
|
@ -15,23 +14,16 @@
|
||||||
"48": "images/icon/icon_48.png",
|
"48": "images/icon/icon_48.png",
|
||||||
"96": "images/icon/icon_96.png"
|
"96": "images/icon/icon_96.png"
|
||||||
},
|
},
|
||||||
"options_ui": {
|
"content_scripts": [{
|
||||||
"page": "html/settings.html",
|
|
||||||
"open_in_tab": false
|
|
||||||
},
|
|
||||||
"content_scripts": [
|
|
||||||
{
|
|
||||||
"matches": [
|
"matches": [
|
||||||
"*://aniwatch.me/*"
|
"*://aniwatch.me/*"
|
||||||
],
|
],
|
||||||
"js": [
|
"js": [
|
||||||
"javascript/common.min.js",
|
|
||||||
"javascript/app.min.js"
|
"javascript/app.min.js"
|
||||||
],
|
],
|
||||||
"css": [
|
"css": [
|
||||||
"stylesheets/aniwatchplus.min.css"
|
"stylesheets/aniwatchplus.min.css"
|
||||||
],
|
],
|
||||||
"run_at": "document_end"
|
"run_at": "document_end"
|
||||||
}
|
}]
|
||||||
]
|
|
||||||
}
|
}
|
|
@ -4,7 +4,6 @@
|
||||||
"version": "$version",
|
"version": "$version",
|
||||||
"description": "$description",
|
"description": "$description",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"storage",
|
|
||||||
"*://aniwatch.me/*"
|
"*://aniwatch.me/*"
|
||||||
],
|
],
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
|
@ -15,23 +14,16 @@
|
||||||
"48": "images/icon/icon_48.png",
|
"48": "images/icon/icon_48.png",
|
||||||
"96": "images/icon/icon_96.png"
|
"96": "images/icon/icon_96.png"
|
||||||
},
|
},
|
||||||
"options_ui": {
|
"content_scripts": [{
|
||||||
"page": "html/settings.html",
|
|
||||||
"open_in_tab": false
|
|
||||||
},
|
|
||||||
"content_scripts": [
|
|
||||||
{
|
|
||||||
"matches": [
|
"matches": [
|
||||||
"*://aniwatch.me/*"
|
"*://aniwatch.me/*"
|
||||||
],
|
],
|
||||||
"js": [
|
"js": [
|
||||||
"javascript/common.min.js",
|
|
||||||
"javascript/app.min.js"
|
"javascript/app.min.js"
|
||||||
],
|
],
|
||||||
"css": [
|
"css": [
|
||||||
"stylesheets/aniwatchplus.min.css"
|
"stylesheets/aniwatchplus.min.css"
|
||||||
],
|
],
|
||||||
"run_at": "document_end"
|
"run_at": "document_end"
|
||||||
}
|
}]
|
||||||
]
|
|
||||||
}
|
}
|
|
@ -1,11 +1,10 @@
|
||||||
{
|
{
|
||||||
"name": "Aniwatch Plus",
|
"name": "Aniwatch Plus",
|
||||||
"short_name": "AW+",
|
"short_name": "AW+",
|
||||||
"version": "1.1.0.0",
|
"version": "0.2.1.0",
|
||||||
"version_name": "1.1",
|
"version_name": "0.2.1 Beta",
|
||||||
"description": "Aniwatch Plus is an unofficial extension which provides several UI improvements for https://aniwatch.me.",
|
"description": "Aniwatch Plus is an unofficial extension which provides several UI improvments for https://aniwatch.me.",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"storage",
|
|
||||||
"*://aniwatch.me/*"
|
"*://aniwatch.me/*"
|
||||||
],
|
],
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
|
@ -19,23 +18,16 @@
|
||||||
"48": "images/icon/icon_48.png",
|
"48": "images/icon/icon_48.png",
|
||||||
"96": "images/icon/icon_96.png"
|
"96": "images/icon/icon_96.png"
|
||||||
},
|
},
|
||||||
"options_ui": {
|
"content_scripts": [{
|
||||||
"page": "html/settings.html",
|
|
||||||
"open_in_tab": false
|
|
||||||
},
|
|
||||||
"content_scripts": [
|
|
||||||
{
|
|
||||||
"matches": [
|
"matches": [
|
||||||
"*://aniwatch.me/*"
|
"*://aniwatch.me/*"
|
||||||
],
|
],
|
||||||
"js": [
|
"js": [
|
||||||
"javascript/common.min.js",
|
|
||||||
"javascript/app.min.js"
|
"javascript/app.min.js"
|
||||||
],
|
],
|
||||||
"css": [
|
"css": [
|
||||||
"stylesheets/aniwatchplus.min.css"
|
"stylesheets/aniwatchplus.min.css"
|
||||||
],
|
],
|
||||||
"run_at": "document_end"
|
"run_at": "document_end"
|
||||||
}
|
}]
|
||||||
]
|
|
||||||
}
|
}
|
|
@ -5,7 +5,6 @@
|
||||||
"version_name": "$semanticVersion",
|
"version_name": "$semanticVersion",
|
||||||
"description": "$description",
|
"description": "$description",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"storage",
|
|
||||||
"*://aniwatch.me/*"
|
"*://aniwatch.me/*"
|
||||||
],
|
],
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
|
@ -16,23 +15,16 @@
|
||||||
"48": "images/icon/icon_48.png",
|
"48": "images/icon/icon_48.png",
|
||||||
"96": "images/icon/icon_96.png"
|
"96": "images/icon/icon_96.png"
|
||||||
},
|
},
|
||||||
"options_ui": {
|
"content_scripts": [{
|
||||||
"page": "html/settings.html",
|
|
||||||
"open_in_tab": false
|
|
||||||
},
|
|
||||||
"content_scripts": [
|
|
||||||
{
|
|
||||||
"matches": [
|
"matches": [
|
||||||
"*://aniwatch.me/*"
|
"*://aniwatch.me/*"
|
||||||
],
|
],
|
||||||
"js": [
|
"js": [
|
||||||
"javascript/common.min.js",
|
|
||||||
"javascript/app.min.js"
|
"javascript/app.min.js"
|
||||||
],
|
],
|
||||||
"css": [
|
"css": [
|
||||||
"stylesheets/aniwatchplus.min.css"
|
"stylesheets/aniwatchplus.min.css"
|
||||||
],
|
],
|
||||||
"run_at": "document_end"
|
"run_at": "document_end"
|
||||||
}
|
}]
|
||||||
]
|
|
||||||
}
|
}
|
|
@ -1,8 +1,5 @@
|
||||||
// vars
|
// vars
|
||||||
@import "./vars/colors";
|
@import './vars/colors';
|
||||||
|
|
||||||
// enhancements
|
// enhancements
|
||||||
@import "./enhancements/fontColor";
|
@import './enhancements/lists.scss';
|
||||||
@import "./enhancements/lists";
|
|
||||||
@import "./enhancements/tabs";
|
|
||||||
@import "./enhancements/watch2gether";
|
|
|
@ -1,7 +0,0 @@
|
||||||
.awp{
|
|
||||||
&-fontColor{
|
|
||||||
&-dark{
|
|
||||||
color: $aniwatchDark !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,5 @@
|
||||||
md-list-item {
|
md-list-item {
|
||||||
&:not(:last-child):not(.awp-list-disabled) {
|
&:not(:last-child) {
|
||||||
// added important to override aniwatch new introduced border which has a different, more "aggressive" color
|
border-bottom: 1px solid $gray;
|
||||||
// https://canary.discord.com/channels/541683465599451136/543812424541798421/773621830161268756
|
|
||||||
border-bottom: 1px solid $gray !important;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
md-tab-item {
|
|
||||||
// hide disabled tabs
|
|
||||||
&.md-disabled:not(.awp-hide-unused-disabled) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
.material-icons {
|
|
||||||
// Fix icon position for room creator
|
|
||||||
&[aria-label="Room creator"] {
|
|
||||||
vertical-align: text-top;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.awp {
|
|
||||||
&-w2g {
|
|
||||||
&-chatCounter {
|
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
&-max {
|
|
||||||
animation: shake 0.1s ease-in-out 0.1s 3 alternate;
|
|
||||||
|
|
||||||
@keyframes shake {
|
|
||||||
from {
|
|
||||||
transform: rotate(5deg);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform-origin: center center;
|
|
||||||
transform: rotate(-5deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,2 @@
|
||||||
$aniwatchBlue: #348fff;
|
$aniwatchBlue: #348fff;
|
||||||
$aniwatchDark: #282a39;
|
|
||||||
|
|
||||||
|
|
||||||
$gray: rgba(155, 155, 155, 0.2);
|
$gray: rgba(155, 155, 155, 0.2);
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"esModuleInterop": true,
|
|
||||||
// You have to explicitly set @types to get DefinitelyTyped type definitions
|
|
||||||
"typeRoots": ["node_modules/@types", "node_modules/web-ext-types"],
|
|
||||||
// Default would be DOM,ES6,DOM.Iterable,ScriptHost (see https://www.typescriptlang.org/docs/handbook/compiler-options.html). However the ES 2020 is required for string.prototype.matchAll (see https://stackoverflow.com/a/57298833).
|
|
||||||
"lib": ["ES2020", "DOM", "DOM.Iterable", "ScriptHost"]
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue