mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2024-11-10 09:07:45 +01:00
commit
59826aae6d
22 changed files with 1819 additions and 642 deletions
33
.eslintrc.js
Normal file
33
.eslintrc.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es2021: true,
|
||||
node: true,
|
||||
},
|
||||
extends: [
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
],
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
ecmaVersion: 12,
|
||||
sourceType: "module",
|
||||
},
|
||||
plugins: ["react", "@typescript-eslint"],
|
||||
rules: {
|
||||
// TODO: Remove warn rules when not needed anymore
|
||||
"@typescript-eslint/no-this-alias": "warn",
|
||||
"no-self-assign": "warn",
|
||||
"@typescript-eslint/no-empty-interface": "warn",
|
||||
"@typescript-eslint/ban-types": "warn",
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
version: "detect",
|
||||
},
|
||||
},
|
||||
};
|
File diff suppressed because one or more lines are too long
1085
package-lock.json
generated
1085
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -19,7 +19,11 @@
|
|||
"@types/firefox-webext-browser": "70.0.1",
|
||||
"@types/jest": "^24.0.23",
|
||||
"@types/jquery": "^3.3.31",
|
||||
"@typescript-eslint/eslint-plugin": "^4.9.1",
|
||||
"@typescript-eslint/parser": "^4.9.1",
|
||||
"copy-webpack-plugin": "^6.0.3",
|
||||
"eslint": "^7.15.0",
|
||||
"eslint-plugin-react": "^7.21.5",
|
||||
"jest": "^26.4.0",
|
||||
"rimraf": "^3.0.0",
|
||||
"ts-jest": "^26.2.0",
|
||||
|
@ -47,7 +51,9 @@
|
|||
"dev": "npm run build:dev && concurrently \"npm run web-run\" \"npm run build:watch\"",
|
||||
"dev:firefox": "npm run build:dev:firefox && concurrently \"npm run web-run:firefox\" \"npm run build:watch:firefox\"",
|
||||
"clean": "rimraf dist",
|
||||
"test": "npx jest"
|
||||
"test": "npx jest",
|
||||
"lint": "eslint src",
|
||||
"lint:fix": "eslint src --fix"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
@ -1855,7 +1855,7 @@ THE SOFTWARE.
|
|||
async-each
|
||||
1.0.3 <https://github.com/paulmillr/async-each>
|
||||
license: MIT
|
||||
authors: Paul Miller <https://paulmillr.com/>
|
||||
authors: Paul Miller (https://paulmillr.com/)
|
||||
|
||||
******************************
|
||||
|
||||
|
@ -2640,6 +2640,34 @@ The above copyright notice and this permission notice shall be included in all c
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
******************************
|
||||
|
||||
bindings
|
||||
1.5.0 <https://github.com/TooTallNate/node-bindings>
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2012 Nathan Rajlich <nathan@tootallnate.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
******************************
|
||||
|
||||
bluebird
|
||||
|
@ -4918,6 +4946,32 @@ OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
|
|||
USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
|
||||
******************************
|
||||
|
||||
file-uri-to-path
|
||||
1.0.0 <https://github.com/TooTallNate/file-uri-to-path>
|
||||
Copyright (c) 2014 Nathan Rajlich <nathan@tootallnate.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
******************************
|
||||
|
||||
fill-range
|
||||
|
@ -5153,6 +5207,34 @@ the licensed code:
|
|||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
******************************
|
||||
|
||||
fsevents
|
||||
2.1.3 <https://github.com/fsevents/fsevents>
|
||||
MIT License
|
||||
-----------
|
||||
|
||||
Copyright (C) 2010-2020 by Philipp Dunkel, Ben Noordhuis, Elan Shankar, Paul Miller
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
******************************
|
||||
|
||||
gensync
|
||||
|
@ -6709,6 +6791,25 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||
SOFTWARE.
|
||||
|
||||
|
||||
******************************
|
||||
|
||||
nan
|
||||
2.14.1 <https://github.com/nodejs/nan>
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright (c) 2018 NAN contributors
|
||||
-----------------------------------
|
||||
|
||||
*NAN contributors listed at <https://github.com/nodejs/nan#contributors>*
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
******************************
|
||||
|
||||
nanomatch
|
||||
|
@ -8350,7 +8451,7 @@ SOFTWARE.
|
|||
run-queue
|
||||
1.0.3 <https://github.com/iarna/run-queue>
|
||||
license: ISC
|
||||
authors: Rebecca Turner <me@re-becca.org>
|
||||
authors: Rebecca Turner <me@re-becca.org> (http://re-becca.org/)
|
||||
|
||||
******************************
|
||||
|
||||
|
@ -9336,7 +9437,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
spdx-license-ids
|
||||
3.0.5 <https://github.com/shinnn/spdx-license-ids>
|
||||
license: CC0-1.0
|
||||
authors: Shinnosuke Watanabe <https://github.com/shinnn>
|
||||
authors: Shinnosuke Watanabe (https://github.com/shinnn)
|
||||
|
||||
******************************
|
||||
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
import * as CompileConfig from "../config.json";
|
||||
|
||||
import Config from "./config";
|
||||
import { Registration } from "./types";
|
||||
// Make the config public for debugging purposes
|
||||
(<any> window).SB = Config;
|
||||
|
||||
import Utils from "./utils";
|
||||
var utils = new Utils({
|
||||
const utils = new Utils({
|
||||
registerFirefoxContentScript,
|
||||
unregisterFirefoxContentScript
|
||||
});
|
||||
|
||||
// Used only on Firefox, which does not support non persistent background pages.
|
||||
var contentScriptRegistrations = {};
|
||||
const contentScriptRegistrations = {};
|
||||
|
||||
// Register content script if needed
|
||||
if (utils.isFirefox()) {
|
||||
|
@ -58,6 +59,7 @@ chrome.runtime.onMessage.addListener(function (request, sender, callback) {
|
|||
iconUrl: "./icons/LogoSponsorBlocker256px.png"
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "registerContentScript":
|
||||
registerFirefoxContentScript(request);
|
||||
return false;
|
||||
|
@ -93,8 +95,8 @@ chrome.runtime.onInstalled.addListener(function (object) {
|
|||
*
|
||||
* @param {JSON} options
|
||||
*/
|
||||
function registerFirefoxContentScript(options) {
|
||||
let oldRegistration = contentScriptRegistrations[options.id];
|
||||
function registerFirefoxContentScript(options: Registration) {
|
||||
const oldRegistration = contentScriptRegistrations[options.id];
|
||||
if (oldRegistration) oldRegistration.unregister();
|
||||
|
||||
browser.contentScripts.register({
|
||||
|
@ -124,10 +126,10 @@ async function submitVote(type: number, UUID: string, category: string) {
|
|||
Config.config.userID = userID;
|
||||
}
|
||||
|
||||
let typeSection = (type !== undefined) ? "&type=" + type : "&category=" + category;
|
||||
const typeSection = (type !== undefined) ? "&type=" + type : "&category=" + category;
|
||||
|
||||
//publish this vote
|
||||
let response = await asyncRequestToServer("POST", "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + typeSection);
|
||||
const response = await asyncRequestToServer("POST", "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + typeSection);
|
||||
|
||||
if (response.ok) {
|
||||
return {
|
||||
|
@ -149,7 +151,7 @@ async function submitVote(type: number, UUID: string, category: string) {
|
|||
}
|
||||
|
||||
async function asyncRequestToServer(type: string, address: string, data = {}) {
|
||||
let serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
|
||||
const serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
|
||||
|
||||
return await (sendRequestToCustomServer(type, serverAddress + address, data));
|
||||
}
|
||||
|
@ -165,8 +167,8 @@ async function sendRequestToCustomServer(type: string, url: string, data = {}) {
|
|||
// If GET, convert JSON to parameters
|
||||
if (type.toLowerCase() === "get") {
|
||||
for (const key in data) {
|
||||
let seperator = url.includes("?") ? "&" : "?";
|
||||
let value = (typeof(data[key]) === "string") ? data[key]: JSON.stringify(data[key]);
|
||||
const seperator = url.includes("?") ? "&" : "?";
|
||||
const value = (typeof(data[key]) === "string") ? data[key]: JSON.stringify(data[key]);
|
||||
url += seperator + key + "=" + value;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ class CategoryChooserComponent extends React.Component<CategoryChooserProps, Cat
|
|||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
render(): React.ReactElement {
|
||||
return (
|
||||
<table id="categoryChooserTable"
|
||||
className="categoryChooserTable">
|
||||
|
@ -55,7 +55,7 @@ class CategoryChooserComponent extends React.Component<CategoryChooserProps, Cat
|
|||
}
|
||||
|
||||
getCategorySkipOptions(): JSX.Element[] {
|
||||
let elements: JSX.Element[] = [];
|
||||
const elements: JSX.Element[] = [];
|
||||
|
||||
for (const category of CompileConfig.categoryList) {
|
||||
elements.push(
|
||||
|
|
|
@ -29,7 +29,7 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
|
|||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
render(): React.ReactElement {
|
||||
let defaultOption = "disable";
|
||||
// Set the default opton properly
|
||||
for (const categorySelection of Config.config.categorySelections) {
|
||||
|
@ -145,9 +145,9 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
|
|||
}
|
||||
|
||||
getCategorySkipOptions(): JSX.Element[] {
|
||||
let elements: JSX.Element[] = [];
|
||||
const elements: JSX.Element[] = [];
|
||||
|
||||
let optionNames = ["disable", "showOverlay", "manualSkip", "autoSkip"];
|
||||
const optionNames = ["disable", "showOverlay", "manualSkip", "autoSkip"];
|
||||
|
||||
for (const optionName of optionNames) {
|
||||
elements.push(
|
||||
|
@ -160,7 +160,7 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
|
|||
return elements;
|
||||
}
|
||||
|
||||
setColorState(event: React.FormEvent<HTMLInputElement>, preview: boolean) {
|
||||
setColorState(event: React.FormEvent<HTMLInputElement>, preview: boolean): void {
|
||||
if (preview) {
|
||||
this.setState({
|
||||
previewColor: event.currentTarget.value
|
||||
|
|
|
@ -35,7 +35,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
|||
constructor(props: NoticeProps) {
|
||||
super(props);
|
||||
|
||||
let maxCountdownTime = () => {
|
||||
const maxCountdownTime = () => {
|
||||
if (this.props.maxCountdownTime) return this.props.maxCountdownTime();
|
||||
else return 4;
|
||||
};
|
||||
|
@ -60,12 +60,12 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
|||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
componentDidMount(): void {
|
||||
this.startCountdown();
|
||||
}
|
||||
|
||||
render() {
|
||||
let noticeStyle: React.CSSProperties = {
|
||||
render(): React.ReactElement {
|
||||
const noticeStyle: React.CSSProperties = {
|
||||
zIndex: this.props.zIndex || (50 + this.amountOfPreviousNotices)
|
||||
}
|
||||
|
||||
|
@ -124,19 +124,19 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
|||
);
|
||||
}
|
||||
|
||||
timerMouseEnter() {
|
||||
timerMouseEnter(): void {
|
||||
if (this.state.countdownManuallyPaused) return;
|
||||
|
||||
this.pauseCountdown();
|
||||
}
|
||||
|
||||
timerMouseLeave() {
|
||||
timerMouseLeave(): void {
|
||||
if (this.state.countdownManuallyPaused) return;
|
||||
|
||||
this.startCountdown();
|
||||
}
|
||||
|
||||
toggleManualPause() {
|
||||
toggleManualPause(): void {
|
||||
this.setState({
|
||||
countdownManuallyPaused: !this.state.countdownManuallyPaused
|
||||
}, () => {
|
||||
|
@ -149,10 +149,10 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
|||
}
|
||||
|
||||
//called every second to lower the countdown before hiding the notice
|
||||
countdown() {
|
||||
countdown(): void {
|
||||
if (!this.props.timed) return;
|
||||
|
||||
let countdownTime = this.state.countdownTime - 1;
|
||||
const countdownTime = this.state.countdownTime - 1;
|
||||
|
||||
if (countdownTime <= 0) {
|
||||
//remove this from setInterval
|
||||
|
@ -166,7 +166,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
|||
|
||||
if (countdownTime == 3) {
|
||||
//start fade out animation
|
||||
let notice = document.getElementById("sponsorSkipNotice" + this.idSuffix);
|
||||
const notice = document.getElementById("sponsorSkipNotice" + this.idSuffix);
|
||||
notice.style.removeProperty("animation");
|
||||
notice.classList.add("sponsorSkipNoticeFadeOut");
|
||||
}
|
||||
|
@ -176,7 +176,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
|||
})
|
||||
}
|
||||
|
||||
pauseCountdown() {
|
||||
pauseCountdown(): void {
|
||||
if (!this.props.timed) return;
|
||||
|
||||
//remove setInterval
|
||||
|
@ -190,12 +190,12 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
|||
});
|
||||
|
||||
//remove the fade out class if it exists
|
||||
let notice = document.getElementById("sponsorSkipNotice" + this.idSuffix);
|
||||
const notice = document.getElementById("sponsorSkipNotice" + this.idSuffix);
|
||||
notice.classList.remove("sponsorSkipNoticeFadeOut");
|
||||
notice.style.animation = "none";
|
||||
}
|
||||
|
||||
startCountdown() {
|
||||
startCountdown(): void {
|
||||
if (!this.props.timed) return;
|
||||
|
||||
//if it has already started, don't start it again
|
||||
|
@ -209,7 +209,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
|||
this.countdownInterval = setInterval(this.countdown.bind(this), 1000);
|
||||
}
|
||||
|
||||
resetCountdown() {
|
||||
resetCountdown(): void {
|
||||
if (!this.props.timed) return;
|
||||
|
||||
this.setState({
|
||||
|
@ -221,36 +221,36 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
|||
/**
|
||||
* @param silent If true, the close listener will not be called
|
||||
*/
|
||||
close(silent?: boolean) {
|
||||
close(silent?: boolean): void {
|
||||
//remove setInterval
|
||||
if (this.countdownInterval !== null) clearInterval(this.countdownInterval);
|
||||
|
||||
if (!silent) this.props.closeListener();
|
||||
}
|
||||
|
||||
changeNoticeTitle(title) {
|
||||
changeNoticeTitle(title: string): void {
|
||||
this.setState({
|
||||
noticeTitle: title
|
||||
});
|
||||
}
|
||||
|
||||
addNoticeInfoMessage(message: string, message2: string = "") {
|
||||
addNoticeInfoMessage(message: string, message2 = ""): void {
|
||||
//TODO: Replace
|
||||
|
||||
let previousInfoMessage = document.getElementById("sponsorTimesInfoMessage" + this.idSuffix);
|
||||
const previousInfoMessage = document.getElementById("sponsorTimesInfoMessage" + this.idSuffix);
|
||||
if (previousInfoMessage != null) {
|
||||
//remove it
|
||||
document.getElementById("sponsorSkipNotice" + this.idSuffix).removeChild(previousInfoMessage);
|
||||
}
|
||||
|
||||
let previousInfoMessage2 = document.getElementById("sponsorTimesInfoMessage" + this.idSuffix + "2");
|
||||
const previousInfoMessage2 = document.getElementById("sponsorTimesInfoMessage" + this.idSuffix + "2");
|
||||
if (previousInfoMessage2 != null) {
|
||||
//remove it
|
||||
document.getElementById("sponsorSkipNotice" + this.idSuffix).removeChild(previousInfoMessage2);
|
||||
}
|
||||
|
||||
//add info
|
||||
let thanksForVotingText = document.createElement("p");
|
||||
const thanksForVotingText = document.createElement("p");
|
||||
thanksForVotingText.id = "sponsorTimesInfoMessage" + this.idSuffix;
|
||||
thanksForVotingText.className = "sponsorTimesInfoMessage";
|
||||
thanksForVotingText.innerText = message;
|
||||
|
@ -259,7 +259,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
|||
document.querySelector("#sponsorSkipNotice" + this.idSuffix + " > tbody").insertBefore(thanksForVotingText, document.getElementById("sponsorSkipNoticeSpacer" + this.idSuffix));
|
||||
|
||||
if (message2 !== undefined) {
|
||||
let thanksForVotingText2 = document.createElement("p");
|
||||
const thanksForVotingText2 = document.createElement("p");
|
||||
thanksForVotingText2.id = "sponsorTimesInfoMessage" + this.idSuffix + "2";
|
||||
thanksForVotingText2.className = "sponsorTimesInfoMessage";
|
||||
thanksForVotingText2.innerText = message2;
|
||||
|
|
|
@ -16,8 +16,8 @@ class NoticeTextSelectionComponent extends React.Component<NoticeTextSelectionPr
|
|||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
let style: React.CSSProperties = {};
|
||||
render(): React.ReactElement {
|
||||
const style: React.CSSProperties = {};
|
||||
if (this.props.onClick) {
|
||||
style.cursor = "pointer";
|
||||
style.textDecoration = "underline"
|
||||
|
|
|
@ -4,7 +4,7 @@ import Config from "../config"
|
|||
import { ContentContainer, SponsorHideType, SponsorTime } from "../types";
|
||||
|
||||
import Utils from "../utils";
|
||||
var utils = new Utils();
|
||||
const utils = new Utils();
|
||||
|
||||
import NoticeComponent from "./NoticeComponent";
|
||||
import NoticeTextSelectionComponent from "./NoticeTextSectionComponent";
|
||||
|
@ -31,7 +31,7 @@ export interface SkipNoticeState {
|
|||
noticeTitle: string;
|
||||
|
||||
messages: string[];
|
||||
messageOnClick: (event: React.MouseEvent) => any;
|
||||
messageOnClick: (event: React.MouseEvent) => unknown;
|
||||
|
||||
countdownTime: number;
|
||||
maxCountdownTime: () => number;
|
||||
|
@ -56,7 +56,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
amountOfPreviousNotices: number;
|
||||
audio: HTMLAudioElement;
|
||||
|
||||
idSuffix: any;
|
||||
idSuffix: string;
|
||||
|
||||
noticeRef: React.MutableRefObject<NoticeComponent>;
|
||||
categoryOptionRef: React.RefObject<HTMLSelectElement>;
|
||||
|
@ -74,7 +74,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
this.contentContainer = props.contentContainer;
|
||||
this.audio = null;
|
||||
|
||||
let categoryName = chrome.i18n.getMessage(this.segments.length > 1 ? "multipleSegments"
|
||||
const categoryName = chrome.i18n.getMessage(this.segments.length > 1 ? "multipleSegments"
|
||||
: "category_" + this.segments[0].category + "_short") || chrome.i18n.getMessage("category_" + this.segments[0].category);
|
||||
let noticeTitle = categoryName + " " + chrome.i18n.getMessage("skipped");
|
||||
if (!this.autoSkip) {
|
||||
|
@ -98,7 +98,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
if (this.amountOfPreviousNotices > 0) {
|
||||
//another notice exists
|
||||
|
||||
let previousNotice = document.getElementsByClassName("sponsorSkipNotice")[0];
|
||||
const previousNotice = document.getElementsByClassName("sponsorSkipNotice")[0];
|
||||
previousNotice.classList.add("secondSkipNotice")
|
||||
}
|
||||
|
||||
|
@ -129,15 +129,15 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
componentDidMount(): void {
|
||||
if (Config.config.audioNotificationOnSkip && this.audio) {
|
||||
this.audio.volume = this.contentContainer().v.volume * 0.1;
|
||||
if (this.autoSkip) this.audio.play();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let noticeStyle: React.CSSProperties = {
|
||||
render(): React.ReactElement {
|
||||
const noticeStyle: React.CSSProperties = {
|
||||
zIndex: 50 + this.amountOfPreviousNotices
|
||||
}
|
||||
if (this.contentContainer().onMobileYouTube) {
|
||||
|
@ -286,7 +286,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
}
|
||||
|
||||
getSubmissionChooser(): JSX.Element[] {
|
||||
let elements: JSX.Element[] = [];
|
||||
const elements: JSX.Element[] = [];
|
||||
|
||||
for (let i = 0; i < this.segments.length; i++) {
|
||||
elements.push(
|
||||
|
@ -301,7 +301,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
return elements;
|
||||
}
|
||||
|
||||
prepAction(action: SkipNoticeAction) {
|
||||
prepAction(action: SkipNoticeAction): void {
|
||||
if (this.segments.length === 1) {
|
||||
this.performAction(0, action);
|
||||
} else {
|
||||
|
@ -321,7 +321,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
);
|
||||
}
|
||||
|
||||
let elements: JSX.Element[] = [];
|
||||
const elements: JSX.Element[] = [];
|
||||
|
||||
for (let i = 0; i < this.state.messages.length; i++) {
|
||||
elements.push(
|
||||
|
@ -341,7 +341,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
*
|
||||
* @param index
|
||||
*/
|
||||
performAction(index: number, action?: SkipNoticeAction) {
|
||||
performAction(index: number, action?: SkipNoticeAction): void {
|
||||
switch (action ?? this.state.actionState) {
|
||||
case SkipNoticeAction.None:
|
||||
break;
|
||||
|
@ -364,7 +364,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
});
|
||||
}
|
||||
|
||||
adjustDownvotingState(value: boolean) {
|
||||
adjustDownvotingState(value: boolean): void {
|
||||
if (!value) this.clearConfigListener();
|
||||
|
||||
this.setState({
|
||||
|
@ -373,14 +373,14 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
});
|
||||
}
|
||||
|
||||
clearConfigListener() {
|
||||
clearConfigListener(): void {
|
||||
if (this.configListener) {
|
||||
Config.configListeners.splice(Config.configListeners.indexOf(this.configListener), 1);
|
||||
this.configListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
openCategoryChooser() {
|
||||
openCategoryChooser(): void {
|
||||
// Add as a config listener
|
||||
this.configListener = () => this.forceUpdate();
|
||||
Config.configListeners.push(this.configListener);
|
||||
|
@ -396,8 +396,8 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
});
|
||||
}
|
||||
|
||||
getCategoryOptions() {
|
||||
let elements = [];
|
||||
getCategoryOptions(): React.ReactElement[] {
|
||||
const elements = [];
|
||||
|
||||
for (const category of Config.config.categorySelections) {
|
||||
elements.push(
|
||||
|
@ -421,7 +421,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
return elements;
|
||||
}
|
||||
|
||||
categorySelectionChange(event: React.ChangeEvent<HTMLSelectElement>) {
|
||||
categorySelectionChange(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||
// See if show more categories was pressed
|
||||
if (event.target.value === "moreCategories") {
|
||||
// Open options page
|
||||
|
@ -433,14 +433,14 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
}
|
||||
}
|
||||
|
||||
unskip(index: number) {
|
||||
unskip(index: number): void {
|
||||
this.contentContainer().unskipSponsorTime(this.segments[index]);
|
||||
|
||||
this.unskippedMode(index, chrome.i18n.getMessage("reskip"));
|
||||
}
|
||||
|
||||
/** Sets up notice to be not skipped yet */
|
||||
unskippedMode(index: number, buttonText: string) {
|
||||
unskippedMode(index: number, buttonText: string): void {
|
||||
//setup new callback and reset countdown
|
||||
this.setState(this.getUnskippedModeInfo(index, buttonText), () => {
|
||||
this.noticeRef.current.resetCountdown();
|
||||
|
@ -448,10 +448,10 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
}
|
||||
|
||||
getUnskippedModeInfo(index: number, buttonText: string) {
|
||||
let self = this;
|
||||
let maxCountdownTime = function() {
|
||||
let sponsorTime = self.segments[index];
|
||||
let duration = Math.round((sponsorTime.segment[1] - self.contentContainer().v.currentTime) * (1 / self.contentContainer().v.playbackRate));
|
||||
const self = this;
|
||||
const maxCountdownTime = function() {
|
||||
const sponsorTime = self.segments[index];
|
||||
const duration = Math.round((sponsorTime.segment[1] - self.contentContainer().v.currentTime) * (1 / self.contentContainer().v.playbackRate));
|
||||
|
||||
return Math.max(duration, 4);
|
||||
};
|
||||
|
@ -468,7 +468,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
}
|
||||
}
|
||||
|
||||
reskip(index: number) {
|
||||
reskip(index: number): void {
|
||||
this.contentContainer().reskipSponsorTime(this.segments[index]);
|
||||
|
||||
//reset countdown
|
||||
|
@ -488,7 +488,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
}
|
||||
}
|
||||
|
||||
afterVote(segment: SponsorTime, type: number, category: string) {
|
||||
afterVote(segment: SponsorTime, type: number, category: string): void {
|
||||
this.addVoteButtonInfo(chrome.i18n.getMessage("voted"));
|
||||
|
||||
if (type === 0) {
|
||||
|
@ -508,32 +508,32 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
}
|
||||
}
|
||||
|
||||
setNoticeInfoMessageWithOnClick(onClick: (event: React.MouseEvent) => any, ...messages: string[]) {
|
||||
setNoticeInfoMessageWithOnClick(onClick: (event: React.MouseEvent) => any, ...messages: string[]): void {
|
||||
this.setState({
|
||||
messages,
|
||||
messageOnClick: (event) => onClick(event)
|
||||
});
|
||||
}
|
||||
|
||||
setNoticeInfoMessage(...messages: string[]) {
|
||||
setNoticeInfoMessage(...messages: string[]): void {
|
||||
this.setState({
|
||||
messages
|
||||
});
|
||||
}
|
||||
|
||||
addVoteButtonInfo(message) {
|
||||
addVoteButtonInfo(message): void {
|
||||
this.setState({
|
||||
thanksForVotingText: message
|
||||
});
|
||||
}
|
||||
|
||||
resetVoteButtonInfo() {
|
||||
resetVoteButtonInfo(): void {
|
||||
this.setState({
|
||||
thanksForVotingText: null
|
||||
});
|
||||
}
|
||||
|
||||
closeListener() {
|
||||
closeListener(): void {
|
||||
this.clearConfigListener();
|
||||
|
||||
this.props.closeListener();
|
||||
|
|
|
@ -6,7 +6,7 @@ import * as CompileConfig from "../../config.json";
|
|||
import Utils from "../utils";
|
||||
import { ContentContainer, SponsorTime } from "../types";
|
||||
import SubmissionNoticeComponent from "./SubmissionNoticeComponent";
|
||||
var utils = new Utils();
|
||||
const utils = new Utils();
|
||||
|
||||
export interface SponsorTimeEditProps {
|
||||
index: number,
|
||||
|
@ -44,7 +44,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
componentDidMount(): void {
|
||||
// Prevent inputs from triggering key events
|
||||
document.getElementById("sponsorTimesContainer" + this.idSuffix).addEventListener('keydown', function (event) {
|
||||
event.stopPropagation();
|
||||
|
@ -57,14 +57,14 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
componentWillUnmount(): void {
|
||||
if (this.configUpdateListener) {
|
||||
Config.configListeners.splice(Config.configListeners.indexOf(this.configUpdate.bind(this)), 1);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let style: React.CSSProperties = {
|
||||
render(): React.ReactElement {
|
||||
const style: React.CSSProperties = {
|
||||
textAlign: "center"
|
||||
};
|
||||
|
||||
|
@ -74,7 +74,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||
|
||||
// This method is required to get !important
|
||||
// https://stackoverflow.com/a/45669262/1985387
|
||||
let oldYouTubeDarkStyles = (node) => {
|
||||
const oldYouTubeDarkStyles = (node) => {
|
||||
if (node) {
|
||||
node.style.setProperty("color", "black", "important");
|
||||
node.style.setProperty("text-shadow", "none", "important");
|
||||
|
@ -83,8 +83,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||
|
||||
// Create time display
|
||||
let timeDisplay: JSX.Element;
|
||||
let sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
|
||||
let segment = sponsorTime.segment;
|
||||
const sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
|
||||
const segment = sponsorTime.segment;
|
||||
if (this.state.editing) {
|
||||
timeDisplay = (
|
||||
<div id={"sponsorTimesContainer" + this.idSuffix}
|
||||
|
@ -102,7 +102,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||
type="text"
|
||||
value={this.state.sponsorTimeEdits[0]}
|
||||
onChange={(e) => {
|
||||
let sponsorTimeEdits = this.state.sponsorTimeEdits;
|
||||
const sponsorTimeEdits = this.state.sponsorTimeEdits;
|
||||
sponsorTimeEdits[0] = e.target.value;
|
||||
|
||||
this.setState({sponsorTimeEdits});
|
||||
|
@ -121,7 +121,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||
type="text"
|
||||
value={this.state.sponsorTimeEdits[1]}
|
||||
onChange={(e) => {
|
||||
let sponsorTimeEdits = this.state.sponsorTimeEdits;
|
||||
const sponsorTimeEdits = this.state.sponsorTimeEdits;
|
||||
sponsorTimeEdits[1] = e.target.value;
|
||||
|
||||
this.setState({sponsorTimeEdits});
|
||||
|
@ -215,8 +215,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||
);
|
||||
}
|
||||
|
||||
getCategoryOptions() {
|
||||
let elements = [(
|
||||
getCategoryOptions(): React.ReactElement[] {
|
||||
const elements = [(
|
||||
<option value={"chooseACategory"}
|
||||
key={"chooseACategory"}>
|
||||
{chrome.i18n.getMessage("chooseACategory")}
|
||||
|
@ -245,7 +245,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||
return elements;
|
||||
}
|
||||
|
||||
categorySelectionChange(event: React.ChangeEvent<HTMLSelectElement>) {
|
||||
categorySelectionChange(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||
// See if show more categories was pressed
|
||||
if (event.target.value === "moreCategories") {
|
||||
// Open options page
|
||||
|
@ -259,16 +259,16 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||
this.saveEditTimes();
|
||||
}
|
||||
|
||||
setTimeToNow(index: number) {
|
||||
setTimeToNow(index: number): void {
|
||||
this.setTimeTo(index, this.props.contentContainer().getRealCurrentTime());
|
||||
}
|
||||
|
||||
setTimeToEnd() {
|
||||
setTimeToEnd(): void {
|
||||
this.setTimeTo(1, this.props.contentContainer().v.duration);
|
||||
}
|
||||
|
||||
setTimeTo(index: number, time: number) {
|
||||
let sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
|
||||
setTimeTo(index: number, time: number): void {
|
||||
const sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
|
||||
|
||||
sponsorTime.segment[index] =
|
||||
time;
|
||||
|
@ -287,7 +287,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||
|
||||
this.saveEditTimes();
|
||||
} else {
|
||||
let sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
|
||||
const sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
|
||||
|
||||
this.setState({
|
||||
editing: true,
|
||||
|
@ -302,8 +302,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||
utils.getFormattedTime(sponsorTime.segment[1], true)];
|
||||
}
|
||||
|
||||
saveEditTimes() {
|
||||
let sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting;
|
||||
saveEditTimes(): void {
|
||||
const sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting;
|
||||
|
||||
if (this.state.editing) {
|
||||
const startTime = utils.getFormattedTimeToSeconds(this.state.sponsorTimeEdits[0]);
|
||||
|
@ -323,26 +323,26 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||
}
|
||||
|
||||
previewTime(): void {
|
||||
let sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
|
||||
let index = this.props.index;
|
||||
const sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
|
||||
const index = this.props.index;
|
||||
|
||||
let skipTime = sponsorTimes[index].segment[0];
|
||||
const skipTime = sponsorTimes[index].segment[0];
|
||||
|
||||
this.props.contentContainer().previewTime(skipTime - 2);
|
||||
}
|
||||
|
||||
inspectTime(): void {
|
||||
let sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
|
||||
let index = this.props.index;
|
||||
const sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
|
||||
const index = this.props.index;
|
||||
|
||||
let skipTime = sponsorTimes[index].segment[0];
|
||||
const skipTime = sponsorTimes[index].segment[0];
|
||||
|
||||
this.props.contentContainer().previewTime(skipTime + 0.000001, false);
|
||||
}
|
||||
|
||||
deleteTime(): void {
|
||||
let sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
|
||||
let index = this.props.index;
|
||||
const sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
|
||||
const index = this.props.index;
|
||||
|
||||
//if it is not a complete sponsor time
|
||||
if (sponsorTimes[index].segment.length < 2) {
|
||||
|
@ -369,7 +369,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||
}
|
||||
}
|
||||
|
||||
configUpdate() {
|
||||
configUpdate(): void {
|
||||
this.forceUpdate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ export interface SubmissionNoticeProps {
|
|||
// Contains functions and variables from the content script needed by the skip notice
|
||||
contentContainer: ContentContainer;
|
||||
|
||||
callback: () => any;
|
||||
callback: () => unknown;
|
||||
|
||||
closeListener: () => void
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
|
|||
// Contains functions and variables from the content script needed by the skip notice
|
||||
contentContainer: ContentContainer;
|
||||
|
||||
callback: () => any;
|
||||
callback: () => unknown;
|
||||
|
||||
noticeRef: React.MutableRefObject<NoticeComponent>;
|
||||
timeEditRefs: React.RefObject<SponsorTimeEditComponent>[];
|
||||
|
@ -39,7 +39,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
|
|||
this.contentContainer = props.contentContainer;
|
||||
this.callback = props.callback;
|
||||
|
||||
let noticeTitle = chrome.i18n.getMessage("confirmNoticeTitle");
|
||||
const noticeTitle = chrome.i18n.getMessage("confirmNoticeTitle");
|
||||
|
||||
// Setup state
|
||||
this.state = {
|
||||
|
@ -49,7 +49,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
|
|||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
componentDidMount(): void {
|
||||
// Catch and rerender when the video size changes
|
||||
//TODO: Use ResizeObserver when it is supported in TypeScript
|
||||
this.videoObserver = new MutationObserver(() => {
|
||||
|
@ -61,13 +61,13 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
|
|||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
componentWillUnmount(): void {
|
||||
if (this.videoObserver) {
|
||||
this.videoObserver.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
render(): React.ReactElement {
|
||||
return (
|
||||
<NoticeComponent noticeTitle={this.state.noticeTitle}
|
||||
idSuffix={this.state.idSuffix}
|
||||
|
@ -114,13 +114,13 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
|
|||
}
|
||||
|
||||
getSponsorTimeMessages(): JSX.Element[] | JSX.Element {
|
||||
let elements: JSX.Element[] = [];
|
||||
const elements: JSX.Element[] = [];
|
||||
this.timeEditRefs = [];
|
||||
|
||||
let sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
|
||||
const sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
|
||||
|
||||
for (let i = 0; i < sponsorTimes.length; i++) {
|
||||
let timeRef = React.createRef<SponsorTimeEditComponent>();
|
||||
const timeRef = React.createRef<SponsorTimeEditComponent>();
|
||||
|
||||
elements.push(
|
||||
<SponsorTimeEditComponent key={i}
|
||||
|
@ -139,7 +139,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
|
|||
}
|
||||
|
||||
getMessageBoxes(): JSX.Element[] | JSX.Element {
|
||||
let elements: JSX.Element[] = [];
|
||||
const elements: JSX.Element[] = [];
|
||||
|
||||
for (let i = 0; i < this.state.messages.length; i++) {
|
||||
elements.push(
|
||||
|
@ -153,7 +153,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
|
|||
return elements;
|
||||
}
|
||||
|
||||
cancel() {
|
||||
cancel(): void {
|
||||
this.noticeRef.current.close(true);
|
||||
|
||||
this.contentContainer().resetSponsorSubmissionNotice();
|
||||
|
@ -161,13 +161,13 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
|
|||
this.props.closeListener();
|
||||
}
|
||||
|
||||
submit() {
|
||||
submit(): void {
|
||||
// save all items
|
||||
for (const ref of this.timeEditRefs) {
|
||||
ref.current.saveEditTimes();
|
||||
}
|
||||
|
||||
let sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting;
|
||||
const sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting;
|
||||
for (const sponsorTime of sponsorTimesSubmitting) {
|
||||
if (sponsorTime.category === "chooseACategory") {
|
||||
alert(chrome.i18n.getMessage("youMustSelectACategory"));
|
||||
|
|
|
@ -132,7 +132,7 @@ class SBMap<T, U> extends Map {
|
|||
}
|
||||
}
|
||||
|
||||
var Config: SBObject = {
|
||||
const Config: SBObject = {
|
||||
/**
|
||||
* Callback function when an option is updated
|
||||
*/
|
||||
|
@ -286,7 +286,7 @@ function configProxy(): any {
|
|||
}
|
||||
});
|
||||
|
||||
var handler: ProxyHandler<any> = {
|
||||
const handler: ProxyHandler<any> = {
|
||||
set(obj, prop, value) {
|
||||
Config.localConfig[prop] = value;
|
||||
|
||||
|
@ -298,7 +298,7 @@ function configProxy(): any {
|
|||
},
|
||||
|
||||
get(obj, prop): any {
|
||||
let data = Config.localConfig[prop];
|
||||
const data = Config.localConfig[prop];
|
||||
|
||||
return obj[prop] || data;
|
||||
},
|
||||
|
@ -314,7 +314,7 @@ function configProxy(): any {
|
|||
return new Proxy({handler}, handler);
|
||||
}
|
||||
|
||||
function fetchConfig() {
|
||||
function fetchConfig(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
chrome.storage.sync.get(null, function(items) {
|
||||
Config.localConfig = <SBConfig> <unknown> items; // Data is ready
|
||||
|
@ -351,7 +351,7 @@ function migrateOldFormats(config: SBConfig) {
|
|||
if (config.whitelistedChannels.length > 0 &&
|
||||
(config.whitelistedChannels[0] == null || config.whitelistedChannels[0].includes("/"))) {
|
||||
const channelURLFixer = async() => {
|
||||
let newChannelList: string[] = [];
|
||||
const newChannelList: string[] = [];
|
||||
for (const item of config.whitelistedChannels) {
|
||||
if (item != null) {
|
||||
if (item.includes("/channel/")) {
|
||||
|
@ -360,7 +360,7 @@ function migrateOldFormats(config: SBConfig) {
|
|||
|
||||
|
||||
// Replace channel URL with channelID
|
||||
let response = await utils.asyncRequestToCustomServer("GET", "https://sponsor.ajay.app/invidious/api/v1/channels/" + item.split("/")[2] + "?fields=authorId");
|
||||
const response = await utils.asyncRequestToCustomServer("GET", "https://sponsor.ajay.app/invidious/api/v1/channels/" + item.split("/")[2] + "?fields=authorId");
|
||||
|
||||
if (response.ok) {
|
||||
newChannelList.push((JSON.parse(response.responseText)).authorId);
|
||||
|
@ -408,9 +408,9 @@ function migrateOldFormats(config: SBConfig) {
|
|||
|
||||
// Otherwise junk data
|
||||
if (Array.isArray(jsonData)) {
|
||||
let oldMap = new Map(jsonData);
|
||||
const oldMap = new Map(jsonData);
|
||||
oldMap.forEach((sponsorTimes: number[][], key) => {
|
||||
let segmentTimes: SponsorTime[] = [];
|
||||
const segmentTimes: SponsorTime[] = [];
|
||||
for (const segment of sponsorTimes) {
|
||||
segmentTimes.push({
|
||||
segment: segment,
|
||||
|
@ -442,7 +442,7 @@ async function setupConfig() {
|
|||
// Reset config
|
||||
function resetConfig() {
|
||||
Config.config = Config.defaults;
|
||||
};
|
||||
}
|
||||
|
||||
function convertJSON(): void {
|
||||
Object.keys(Config.localConfig).forEach(key => {
|
||||
|
@ -453,17 +453,17 @@ function convertJSON(): void {
|
|||
// Add defaults
|
||||
function addDefaults() {
|
||||
for (const key in Config.defaults) {
|
||||
if(!Config.localConfig.hasOwnProperty(key)) {
|
||||
if(!Object.prototype.hasOwnProperty.call(Config.localConfig, key)) {
|
||||
Config.localConfig[key] = Config.defaults[key];
|
||||
} else if (key === "barTypes") {
|
||||
for (const key2 in Config.defaults[key]) {
|
||||
if(!Config.localConfig[key].hasOwnProperty(key2)) {
|
||||
if(!Object.prototype.hasOwnProperty.call(Config.localConfig[key], key2)) {
|
||||
Config.localConfig[key][key2] = Config.defaults[key][key2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Sync config
|
||||
setupConfig();
|
||||
|
|
210
src/content.ts
210
src/content.ts
|
@ -4,7 +4,7 @@ import { SponsorTime, CategorySkipOption, CategorySelection, VideoID, SponsorHid
|
|||
|
||||
import { ContentContainer } from "./types";
|
||||
import Utils from "./utils";
|
||||
var utils = new Utils();
|
||||
const utils = new Utils();
|
||||
|
||||
import runThePopup from "./popup";
|
||||
|
||||
|
@ -17,80 +17,80 @@ import SubmissionNotice from "./render/SubmissionNotice";
|
|||
utils.wait(() => Config.config !== null, 5000, 10).then(addCSS);
|
||||
|
||||
//was sponsor data found when doing SponsorsLookup
|
||||
var sponsorDataFound = false;
|
||||
var previousVideoID: VideoID = null;
|
||||
let sponsorDataFound = false;
|
||||
let previousVideoID: VideoID = null;
|
||||
//the actual sponsorTimes if loaded and UUIDs associated with them
|
||||
var sponsorTimes: SponsorTime[] = null;
|
||||
let sponsorTimes: SponsorTime[] = null;
|
||||
//what video id are these sponsors for
|
||||
var sponsorVideoID: VideoID = null;
|
||||
let sponsorVideoID: VideoID = null;
|
||||
|
||||
// JSON video info
|
||||
var videoInfo: any = null;
|
||||
let videoInfo: any = null;
|
||||
//the channel this video is about
|
||||
var channelID;
|
||||
let channelID;
|
||||
|
||||
// Skips are scheduled to ensure precision.
|
||||
// Skips are rescheduled every seeking event.
|
||||
// Skips are canceled every seeking event
|
||||
var currentSkipSchedule: NodeJS.Timeout = null;
|
||||
var seekListenerSetUp = false
|
||||
let currentSkipSchedule: NodeJS.Timeout = null;
|
||||
let seekListenerSetUp = false
|
||||
|
||||
/** @type {Array[boolean]} Has the sponsor been skipped */
|
||||
var sponsorSkipped: boolean[] = [];
|
||||
let sponsorSkipped: boolean[] = [];
|
||||
|
||||
//the video
|
||||
var video: HTMLVideoElement;
|
||||
let video: HTMLVideoElement;
|
||||
|
||||
var onInvidious;
|
||||
var onMobileYouTube;
|
||||
let onInvidious;
|
||||
let onMobileYouTube;
|
||||
|
||||
//the video id of the last preview bar update
|
||||
var lastPreviewBarUpdate;
|
||||
let lastPreviewBarUpdate;
|
||||
|
||||
//whether the duration listener listening for the duration changes of the video has been setup yet
|
||||
var durationListenerSetUp = false;
|
||||
let durationListenerSetUp = false;
|
||||
|
||||
// Is the video currently being switched
|
||||
var switchingVideos = null;
|
||||
let switchingVideos = null;
|
||||
|
||||
// Used by the play and playing listeners to make sure two aren't
|
||||
// called at the same time
|
||||
var lastCheckTime = 0;
|
||||
var lastCheckVideoTime = -1;
|
||||
let lastCheckTime = 0;
|
||||
let lastCheckVideoTime = -1;
|
||||
|
||||
//is this channel whitelised from getting sponsors skipped
|
||||
var channelWhitelisted = false;
|
||||
let channelWhitelisted = false;
|
||||
|
||||
// create preview bar
|
||||
var previewBar: PreviewBar = null;
|
||||
let previewBar: PreviewBar = null;
|
||||
|
||||
//the player controls on the YouTube player
|
||||
var controls = null;
|
||||
let controls = null;
|
||||
|
||||
// Direct Links after the config is loaded
|
||||
utils.wait(() => Config.config !== null, 1000, 1).then(() => videoIDChange(getYouTubeVideoID(document.URL)));
|
||||
|
||||
//the amount of times the sponsor lookup has retried
|
||||
//this only happens if there is an error
|
||||
var sponsorLookupRetries = 0;
|
||||
let sponsorLookupRetries = 0;
|
||||
|
||||
//if showing the start sponsor button or the end sponsor button on the player
|
||||
var showingStartSponsor = true;
|
||||
let showingStartSponsor = true;
|
||||
|
||||
//the sponsor times being prepared to be submitted
|
||||
var sponsorTimesSubmitting: SponsorTime[] = [];
|
||||
let sponsorTimesSubmitting: SponsorTime[] = [];
|
||||
|
||||
//becomes true when isInfoFound is called
|
||||
//this is used to close the popup on YouTube when the other popup opens
|
||||
var popupInitialised = false;
|
||||
let popupInitialised = false;
|
||||
|
||||
var submissionNotice: SubmissionNotice = null;
|
||||
let submissionNotice: SubmissionNotice = null;
|
||||
|
||||
// If there is an advert playing (or about to be played), this is true
|
||||
var isAdPlaying = false;
|
||||
let isAdPlaying = false;
|
||||
|
||||
// Contains all of the functions and variables needed by the skip notice
|
||||
var skipNoticeContentContainer: ContentContainer = () => ({
|
||||
const skipNoticeContentContainer: ContentContainer = () => ({
|
||||
vote,
|
||||
dontShowNoticeAgain,
|
||||
unskipSponsorTime,
|
||||
|
@ -218,13 +218,13 @@ if (!Config.configListeners.includes(contentConfigUpdateListener)) {
|
|||
|
||||
//check for hotkey pressed
|
||||
document.onkeydown = function(e: KeyboardEvent){
|
||||
var key = e.key;
|
||||
const key = e.key;
|
||||
|
||||
let video = document.getElementById("movie_player");
|
||||
const video = document.getElementById("movie_player");
|
||||
|
||||
let startSponsorKey = Config.config.startSponsorKeybind;
|
||||
const startSponsorKey = Config.config.startSponsorKeybind;
|
||||
|
||||
let submitKey = Config.config.submitKeybind;
|
||||
const submitKey = Config.config.submitKeybind;
|
||||
|
||||
//is the video in focus, otherwise they could be typing a comment
|
||||
if (document.activeElement === video) {
|
||||
|
@ -296,7 +296,7 @@ async function videoIDChange(id) {
|
|||
}
|
||||
|
||||
if (isUnlisted()) {
|
||||
let shouldContinue = confirm(chrome.i18n.getMessage("confirmPrivacy"));
|
||||
const shouldContinue = confirm(chrome.i18n.getMessage("confirmPrivacy"));
|
||||
if(!shouldContinue) return;
|
||||
}
|
||||
}
|
||||
|
@ -323,7 +323,7 @@ async function videoIDChange(id) {
|
|||
//warn them if they had unsubmitted times
|
||||
if (previousVideoID != null) {
|
||||
//get the sponsor times from storage
|
||||
let sponsorTimes = Config.config.segmentTimes.get(previousVideoID);
|
||||
const sponsorTimes = Config.config.segmentTimes.get(previousVideoID);
|
||||
if (sponsorTimes != undefined && sponsorTimes.length > 0 && new URL(document.URL).host !== "music.youtube.com") {
|
||||
//warn them that they have unsubmitted sponsor times
|
||||
chrome.runtime.sendMessage({
|
||||
|
@ -347,7 +347,7 @@ async function videoIDChange(id) {
|
|||
//make sure everything is properly added
|
||||
updateVisibilityOfPlayerControlsButton().then(() => {
|
||||
//see if the onvideo control image needs to be changed
|
||||
let segments = Config.config.segmentTimes.get(sponsorVideoID);
|
||||
const segments = Config.config.segmentTimes.get(sponsorVideoID);
|
||||
if (segments != null && segments.length > 0 && segments[segments.length - 1].segment.length >= 2) {
|
||||
changeStartSponsorButton(true, true);
|
||||
} else if (segments != null && segments.length > 0 && segments[segments.length - 1].segment.length < 2) {
|
||||
|
@ -368,7 +368,7 @@ async function videoIDChange(id) {
|
|||
}
|
||||
|
||||
function handleMobileControlsMutations(): void {
|
||||
let mobileYouTubeSelector = ".progress-bar-background";
|
||||
const mobileYouTubeSelector = ".progress-bar-background";
|
||||
|
||||
updateVisibilityOfPlayerControlsButton().then((createdButtons) => {
|
||||
if (createdButtons) {
|
||||
|
@ -470,14 +470,14 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
|
|||
|
||||
if (currentTime === undefined || currentTime === null) currentTime = video.currentTime;
|
||||
|
||||
let skipInfo = getNextSkipIndex(currentTime, includeIntersectingSegments, includeNonIntersectingSegments);
|
||||
const skipInfo = getNextSkipIndex(currentTime, includeIntersectingSegments, includeNonIntersectingSegments);
|
||||
|
||||
if (skipInfo.index === -1) return;
|
||||
|
||||
let currentSkip = skipInfo.array[skipInfo.index];
|
||||
let skipTime: number[] = [currentSkip.segment[0], skipInfo.array[skipInfo.endIndex].segment[1]];
|
||||
let timeUntilSponsor = skipTime[0] - currentTime;
|
||||
let videoID = sponsorVideoID;
|
||||
const currentSkip = skipInfo.array[skipInfo.index];
|
||||
const skipTime: number[] = [currentSkip.segment[0], skipInfo.array[skipInfo.endIndex].segment[1]];
|
||||
const timeUntilSponsor = skipTime[0] - currentTime;
|
||||
const videoID = sponsorVideoID;
|
||||
|
||||
// Find all indexes in between the start and end
|
||||
let skippingSegments = [skipInfo.array[skipInfo.index]];
|
||||
|
@ -496,7 +496,7 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
|
|||
if (utils.getCategorySelection(currentSkip.category)?.option === CategorySkipOption.ShowOverlay
|
||||
&& skipInfo.array !== sponsorTimesSubmitting) return;
|
||||
|
||||
let skippingFunction = () => {
|
||||
const skippingFunction = () => {
|
||||
let forcedSkipTime: number = null;
|
||||
let forcedIncludeIntersectingSegments = false;
|
||||
let forcedIncludeNonIntersectingSegments = true;
|
||||
|
@ -529,7 +529,7 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
|
|||
* This makes sure the videoID is still correct and if the sponsorTime is included
|
||||
*/
|
||||
function incorrectVideoCheck(videoID?: string, sponsorTime?: SponsorTime): boolean {
|
||||
let currentVideoID = getYouTubeVideoID(document.URL);
|
||||
const currentVideoID = getYouTubeVideoID(document.URL);
|
||||
if (currentVideoID !== (videoID || sponsorVideoID) || (sponsorTime && (!sponsorTimes || !sponsorTimes.includes(sponsorTime)) && !sponsorTimesSubmitting.includes(sponsorTime))) {
|
||||
// Something has really gone wrong
|
||||
console.error("[SponsorBlock] The videoID recorded when trying to skip is different than what it should be.");
|
||||
|
@ -614,7 +614,7 @@ async function sponsorsLookup(id: string) {
|
|||
//made true once a setTimeout has been created to try again after a server error
|
||||
let recheckStarted = false;
|
||||
// Create categories list
|
||||
let categories: string[] = [];
|
||||
const categories: string[] = [];
|
||||
for (const categorySelection of Config.config.categorySelections) {
|
||||
categories.push(categorySelection.name);
|
||||
}
|
||||
|
@ -649,7 +649,7 @@ async function sponsorsLookup(id: string) {
|
|||
}
|
||||
}
|
||||
|
||||
let recievedSegments: SponsorTime[] = result;
|
||||
const recievedSegments: SponsorTime[] = result;
|
||||
if (!recievedSegments.length) {
|
||||
console.error("[SponsorBlock] Server returned malformed response: " + JSON.stringify(recievedSegments));
|
||||
return;
|
||||
|
@ -713,7 +713,7 @@ function retryFetch(id: string): void {
|
|||
|
||||
//check if this video was uploaded recently
|
||||
utils.wait(() => !!videoInfo).then(() => {
|
||||
let dateUploaded = videoInfo?.microformat?.playerMicroformatRenderer?.uploadDate;
|
||||
const dateUploaded = videoInfo?.microformat?.playerMicroformatRenderer?.uploadDate;
|
||||
|
||||
//if less than 3 days old
|
||||
if (Date.now() - new Date(dateUploaded).getTime() < 259200000) {
|
||||
|
@ -733,7 +733,7 @@ function retryFetch(id: string): void {
|
|||
function startSkipScheduleCheckingForStartSponsors() {
|
||||
if (!switchingVideos) {
|
||||
// See if there are any starting sponsors
|
||||
let startingSponsor: number = -1;
|
||||
let startingSponsor = -1;
|
||||
for (const time of sponsorTimes) {
|
||||
if (time.segment[0] <= video.currentTime && time.segment[0] > startingSponsor && time.segment[1] > video.currentTime) {
|
||||
startingSponsor = time.segment[0];
|
||||
|
@ -763,7 +763,7 @@ function startSkipScheduleCheckingForStartSponsors() {
|
|||
function getVideoInfo() {
|
||||
sendRequestToCustomServer('GET', "https://www.youtube.com/get_video_info?video_id=" + sponsorVideoID, function(xmlhttp, error) {
|
||||
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
|
||||
let decodedData = decodeURIComponent(xmlhttp.responseText).match(/player_response=([^&]*)/)[1];
|
||||
const decodedData = decodeURIComponent(xmlhttp.responseText).match(/player_response=([^&]*)/)[1];
|
||||
if (!decodedData) {
|
||||
console.error("[SB] Failed at getting video info from YouTube.");
|
||||
return;
|
||||
|
@ -803,7 +803,7 @@ function getYouTubeVideoID(url: string) {
|
|||
|
||||
//Get ID from searchParam
|
||||
if (urlObject.searchParams.has("v") && ["/watch", "/watch/"].includes(urlObject.pathname) || urlObject.pathname.startsWith("/tv/watch")) {
|
||||
let id = urlObject.searchParams.get("v");
|
||||
const id = urlObject.searchParams.get("v");
|
||||
return id.length == 11 ? id : false;
|
||||
} else if (urlObject.pathname.startsWith("/embed/")) {
|
||||
try {
|
||||
|
@ -836,10 +836,10 @@ function updatePreviewBar() {
|
|||
let localSponsorTimes = sponsorTimes;
|
||||
if (localSponsorTimes == null) localSponsorTimes = [];
|
||||
|
||||
let allSponsorTimes = localSponsorTimes.concat(sponsorTimesSubmitting);
|
||||
const allSponsorTimes = localSponsorTimes.concat(sponsorTimesSubmitting);
|
||||
|
||||
//create an array of the sponsor types
|
||||
let types = [];
|
||||
const types = [];
|
||||
for (let i = 0; i < localSponsorTimes.length; i++) {
|
||||
if (localSponsorTimes[i].hidden === SponsorHideType.Visible) {
|
||||
types.push(localSponsorTimes[i].category);
|
||||
|
@ -872,7 +872,7 @@ function whitelistCheck() {
|
|||
}
|
||||
|
||||
//see if this is a whitelisted channel
|
||||
let whitelistedChannels = Config.config.whitelistedChannels;
|
||||
const whitelistedChannels = Config.config.whitelistedChannels;
|
||||
|
||||
if (whitelistedChannels != undefined && whitelistedChannels.includes(channelID)) {
|
||||
channelWhitelisted = true;
|
||||
|
@ -888,17 +888,17 @@ function whitelistCheck() {
|
|||
function getNextSkipIndex(currentTime: number, includeIntersectingSegments: boolean, includeNonIntersectingSegments: boolean):
|
||||
{array: SponsorTime[], index: number, endIndex: number, openNotice: boolean} {
|
||||
|
||||
let sponsorStartTimes = getStartTimes(sponsorTimes, includeIntersectingSegments, includeNonIntersectingSegments);
|
||||
let sponsorStartTimesAfterCurrentTime = getStartTimes(sponsorTimes, includeIntersectingSegments, includeNonIntersectingSegments, currentTime, true, true);
|
||||
const sponsorStartTimes = getStartTimes(sponsorTimes, includeIntersectingSegments, includeNonIntersectingSegments);
|
||||
const sponsorStartTimesAfterCurrentTime = getStartTimes(sponsorTimes, includeIntersectingSegments, includeNonIntersectingSegments, currentTime, true, true);
|
||||
|
||||
let minSponsorTimeIndex = sponsorStartTimes.indexOf(Math.min(...sponsorStartTimesAfterCurrentTime));
|
||||
let endTimeIndex = getLatestEndTimeIndex(sponsorTimes, minSponsorTimeIndex);
|
||||
const minSponsorTimeIndex = sponsorStartTimes.indexOf(Math.min(...sponsorStartTimesAfterCurrentTime));
|
||||
const endTimeIndex = getLatestEndTimeIndex(sponsorTimes, minSponsorTimeIndex);
|
||||
|
||||
let previewSponsorStartTimes = getStartTimes(sponsorTimesSubmitting, includeIntersectingSegments, includeNonIntersectingSegments);
|
||||
let previewSponsorStartTimesAfterCurrentTime = getStartTimes(sponsorTimesSubmitting, includeIntersectingSegments, includeNonIntersectingSegments, currentTime, false, false);
|
||||
const previewSponsorStartTimes = getStartTimes(sponsorTimesSubmitting, includeIntersectingSegments, includeNonIntersectingSegments);
|
||||
const previewSponsorStartTimesAfterCurrentTime = getStartTimes(sponsorTimesSubmitting, includeIntersectingSegments, includeNonIntersectingSegments, currentTime, false, false);
|
||||
|
||||
let minPreviewSponsorTimeIndex = previewSponsorStartTimes.indexOf(Math.min(...previewSponsorStartTimesAfterCurrentTime));
|
||||
let previewEndTimeIndex = getLatestEndTimeIndex(sponsorTimesSubmitting, minPreviewSponsorTimeIndex);
|
||||
const minPreviewSponsorTimeIndex = previewSponsorStartTimes.indexOf(Math.min(...previewSponsorStartTimesAfterCurrentTime));
|
||||
const previewEndTimeIndex = getLatestEndTimeIndex(sponsorTimesSubmitting, minPreviewSponsorTimeIndex);
|
||||
|
||||
if ((minPreviewSponsorTimeIndex === -1 && minSponsorTimeIndex !== -1) ||
|
||||
sponsorStartTimes[minSponsorTimeIndex] < previewSponsorStartTimes[minPreviewSponsorTimeIndex]) {
|
||||
|
@ -931,7 +931,7 @@ function getNextSkipIndex(currentTime: number, includeIntersectingSegments: bool
|
|||
* @param index Index of the given sponsor
|
||||
* @param hideHiddenSponsors
|
||||
*/
|
||||
function getLatestEndTimeIndex(sponsorTimes: SponsorTime[], index: number, hideHiddenSponsors: boolean = true): number {
|
||||
function getLatestEndTimeIndex(sponsorTimes: SponsorTime[], index: number, hideHiddenSponsors = true): number {
|
||||
// Only combine segments for AutoSkip
|
||||
if (index == -1 ||
|
||||
utils.getCategorySelection(sponsorTimes[index].category)?.option !== CategorySkipOption.AutoSkip) return index;
|
||||
|
@ -940,8 +940,8 @@ function getLatestEndTimeIndex(sponsorTimes: SponsorTime[], index: number, hideH
|
|||
let latestEndTimeIndex = index;
|
||||
|
||||
for (let i = 0; i < sponsorTimes?.length; i++) {
|
||||
let currentSegment = sponsorTimes[i].segment;
|
||||
let latestEndTime = sponsorTimes[latestEndTimeIndex].segment[1];
|
||||
const currentSegment = sponsorTimes[i].segment;
|
||||
const latestEndTime = sponsorTimes[latestEndTimeIndex].segment[1];
|
||||
|
||||
if (currentSegment[0] <= latestEndTime && currentSegment[1] > latestEndTime
|
||||
&& (!hideHiddenSponsors || sponsorTimes[i].hidden === SponsorHideType.Visible)
|
||||
|
@ -970,10 +970,10 @@ function getLatestEndTimeIndex(sponsorTimes: SponsorTime[], index: number, hideH
|
|||
* the current time, but end after
|
||||
*/
|
||||
function getStartTimes(sponsorTimes: SponsorTime[], includeIntersectingSegments: boolean, includeNonIntersectingSegments: boolean,
|
||||
minimum?: number, onlySkippableSponsors: boolean = false, hideHiddenSponsors: boolean = false): number[] {
|
||||
minimum?: number, onlySkippableSponsors = false, hideHiddenSponsors = false): number[] {
|
||||
if (sponsorTimes === null) return [];
|
||||
|
||||
let startTimes: number[] = [];
|
||||
const startTimes: number[] = [];
|
||||
|
||||
for (let i = 0; i < sponsorTimes?.length; i++) {
|
||||
if ((minimum === undefined
|
||||
|
@ -1006,7 +1006,7 @@ function previewTime(time: number, unpause = true) {
|
|||
//skip from the start time to the end time for a certain index sponsor time
|
||||
function skipToTime(v: HTMLVideoElement, skipTime: number[], skippingSegments: SponsorTime[], openNotice: boolean) {
|
||||
// There will only be one submission if it is manual skip
|
||||
let autoSkip: boolean = utils.getCategorySelection(skippingSegments[0].category)?.option === CategorySkipOption.AutoSkip;
|
||||
const autoSkip: boolean = utils.getCategorySelection(skippingSegments[0].category)?.option === CategorySkipOption.AutoSkip;
|
||||
|
||||
if ((autoSkip || sponsorTimesSubmitting.includes(skippingSegments[0])) && v.currentTime !== skipTime[1]) {
|
||||
// Fix for looped videos not working when skipping to the end #426
|
||||
|
@ -1031,7 +1031,7 @@ function skipToTime(v: HTMLVideoElement, skipTime: number[], skippingSegments: S
|
|||
let isPreviewSegment = false;
|
||||
|
||||
for (const segment of skippingSegments) {
|
||||
let index = sponsorTimes.indexOf(segment);
|
||||
const index = sponsorTimes.indexOf(segment);
|
||||
if (index !== -1 && !sponsorSkipped[index]) {
|
||||
utils.asyncRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + segment.UUID);
|
||||
|
||||
|
@ -1066,7 +1066,7 @@ function createButton(baseID, title, callback, imageName, isDraggable=false): bo
|
|||
if (document.getElementById(baseID + "Button") != null) return false;
|
||||
|
||||
// Button HTML
|
||||
let newButton = document.createElement("button");
|
||||
const newButton = document.createElement("button");
|
||||
newButton.draggable = isDraggable;
|
||||
newButton.id = baseID + "Button";
|
||||
newButton.classList.add("playerButton");
|
||||
|
@ -1077,7 +1077,7 @@ function createButton(baseID, title, callback, imageName, isDraggable=false): bo
|
|||
});
|
||||
|
||||
// Image HTML
|
||||
let newButtonImage = document.createElement("img");
|
||||
const newButtonImage = document.createElement("img");
|
||||
newButton.draggable = isDraggable;
|
||||
newButtonImage.id = baseID + "Image";
|
||||
newButtonImage.className = "playerButtonImage";
|
||||
|
@ -1092,8 +1092,8 @@ function createButton(baseID, title, callback, imageName, isDraggable=false): bo
|
|||
return true;
|
||||
}
|
||||
|
||||
function getControls(): HTMLElement | boolean {
|
||||
let controlsSelectors = [
|
||||
function getControls(): HTMLElement | false {
|
||||
const controlsSelectors = [
|
||||
// YouTube
|
||||
".ytp-right-controls",
|
||||
// Mobile YouTube
|
||||
|
@ -1103,7 +1103,7 @@ function getControls(): HTMLElement | boolean {
|
|||
]
|
||||
|
||||
for (const controlsSelector of controlsSelectors) {
|
||||
let controls = document.querySelectorAll(controlsSelector);
|
||||
const controls = document.querySelectorAll(controlsSelector);
|
||||
|
||||
if (controls && controls.length > 0) {
|
||||
return <HTMLElement> controls[controls.length - 1];
|
||||
|
@ -1111,13 +1111,13 @@ function getControls(): HTMLElement | boolean {
|
|||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
//adds all the player controls buttons
|
||||
async function createButtons(): Promise<boolean> {
|
||||
if (onMobileYouTube) return;
|
||||
|
||||
let result = await utils.wait(getControls).catch();
|
||||
const result = await utils.wait(getControls).catch();
|
||||
|
||||
//set global controls variable
|
||||
controls = result;
|
||||
|
@ -1138,7 +1138,7 @@ async function updateVisibilityOfPlayerControlsButton(): Promise<boolean> {
|
|||
//not on a proper video yet
|
||||
if (!sponsorVideoID) return false;
|
||||
|
||||
let createdButtons = await createButtons();
|
||||
const createdButtons = await createButtons();
|
||||
|
||||
if (Config.config.hideVideoPlayerControls || onInvidious) {
|
||||
document.getElementById("startSponsorButton").style.display = "none";
|
||||
|
@ -1168,8 +1168,8 @@ async function updateVisibilityOfPlayerControlsButton(): Promise<boolean> {
|
|||
*/
|
||||
function getRealCurrentTime(): number {
|
||||
// Used to check if replay button
|
||||
let playButtonSVGData = document.querySelector(".ytp-play-button")?.querySelector(".ytp-svg-fill")?.getAttribute("d");
|
||||
let replaceSVGData = "M 18,11 V 7 l -5,5 5,5 v -4 c 3.3,0 6,2.7 6,6 0,3.3 -2.7,6 -6,6 -3.3,0 -6,-2.7 -6,-6 h -2 c 0,4.4 3.6,8 8,8 4.4,0 8,-3.6 8,-8 0,-4.4 -3.6,-8 -8,-8 z";
|
||||
const playButtonSVGData = document.querySelector(".ytp-play-button")?.querySelector(".ytp-svg-fill")?.getAttribute("d");
|
||||
const replaceSVGData = "M 18,11 V 7 l -5,5 5,5 v -4 c 3.3,0 6,2.7 6,6 0,3.3 -2.7,6 -6,6 -3.3,0 -6,-2.7 -6,-6 h -2 c 0,4.4 3.6,8 8,8 4.4,0 8,-3.6 8,-8 0,-4.4 -3.6,-8 -8,-8 z";
|
||||
|
||||
if (playButtonSVGData === replaceSVGData) {
|
||||
// At the end of the video
|
||||
|
@ -1204,8 +1204,8 @@ function startSponsorClicked() {
|
|||
updateSponsorTimesSubmitting(false)
|
||||
}
|
||||
|
||||
function updateSponsorTimesSubmitting(getFromConfig: boolean = true) {
|
||||
let segmentTimes = Config.config.segmentTimes.get(sponsorVideoID);
|
||||
function updateSponsorTimesSubmitting(getFromConfig = true) {
|
||||
const segmentTimes = Config.config.segmentTimes.get(sponsorVideoID);
|
||||
|
||||
//see if this data should be saved in the sponsorTimesSubmitting variable
|
||||
if (getFromConfig && segmentTimes != undefined) {
|
||||
|
@ -1234,7 +1234,7 @@ async function changeStartSponsorButton(showStartSponsor, uploadButtonVisible) {
|
|||
if(!sponsorVideoID) return false;
|
||||
|
||||
//if it isn't visible, there is no data
|
||||
let shouldHide = (uploadButtonVisible && !(Config.config.hideDeleteButtonPlayerControls || onInvidious)) ? "unset" : "none"
|
||||
const shouldHide = (uploadButtonVisible && !(Config.config.hideDeleteButtonPlayerControls || onInvidious)) ? "unset" : "none"
|
||||
document.getElementById("deleteButton").style.display = shouldHide;
|
||||
|
||||
if (showStartSponsor) {
|
||||
|
@ -1275,7 +1275,7 @@ function openInfoMenu() {
|
|||
|
||||
sendRequestToCustomServer('GET', chrome.extension.getURL("popup.html"), function(xmlhttp) {
|
||||
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
|
||||
let popup = document.createElement("div");
|
||||
const popup = document.createElement("div");
|
||||
popup.id = "sponsorBlockPopupContainer";
|
||||
|
||||
let htmlData = xmlhttp.responseText;
|
||||
|
@ -1288,7 +1288,7 @@ function openInfoMenu() {
|
|||
popup.innerHTML = htmlData;
|
||||
|
||||
//close button
|
||||
let closeButton = document.createElement("div");
|
||||
const closeButton = document.createElement("div");
|
||||
closeButton.innerText = chrome.i18n.getMessage("closePopup");
|
||||
closeButton.classList.add("smallLink");
|
||||
closeButton.setAttribute("align", "center");
|
||||
|
@ -1299,7 +1299,7 @@ function openInfoMenu() {
|
|||
//add the close button
|
||||
popup.prepend(closeButton);
|
||||
|
||||
let parentNodes = document.querySelectorAll("#secondary");
|
||||
const parentNodes = document.querySelectorAll("#secondary");
|
||||
let parentNode = null;
|
||||
for (let i = 0; i < parentNodes.length; i++) {
|
||||
if (parentNodes[i].firstElementChild !== null) {
|
||||
|
@ -1313,10 +1313,10 @@ function openInfoMenu() {
|
|||
|
||||
//make the logo source not 404
|
||||
//query selector must be used since getElementByID doesn't work on a node and this isn't added to the document yet
|
||||
let logo = <HTMLImageElement> popup.querySelector("#sponsorBlockPopupLogo");
|
||||
let settings = <HTMLImageElement> popup.querySelector("#sbPopupIconSettings");
|
||||
let edit = <HTMLImageElement> popup.querySelector("#sbPopupIconEdit");
|
||||
let check = <HTMLImageElement> popup.querySelector("#sbPopupIconCheck");
|
||||
const logo = <HTMLImageElement> popup.querySelector("#sponsorBlockPopupLogo");
|
||||
const settings = <HTMLImageElement> popup.querySelector("#sbPopupIconSettings");
|
||||
const edit = <HTMLImageElement> popup.querySelector("#sbPopupIconEdit");
|
||||
const check = <HTMLImageElement> popup.querySelector("#sbPopupIconCheck");
|
||||
logo.src = chrome.extension.getURL("icons/LogoSponsorBlocker256px.png");
|
||||
settings.src = chrome.extension.getURL("icons/settings.svg");
|
||||
edit.src = chrome.extension.getURL("icons/pencil.svg");
|
||||
|
@ -1332,7 +1332,7 @@ function openInfoMenu() {
|
|||
}
|
||||
|
||||
function closeInfoMenu() {
|
||||
let popup = document.getElementById("sponsorBlockPopupContainer");
|
||||
const popup = document.getElementById("sponsorBlockPopupContainer");
|
||||
if (popup != null) {
|
||||
popup.remove();
|
||||
|
||||
|
@ -1347,12 +1347,12 @@ function clearSponsorTimes() {
|
|||
//it can't update to this info yet
|
||||
closeInfoMenu();
|
||||
|
||||
let currentVideoID = sponsorVideoID;
|
||||
const currentVideoID = sponsorVideoID;
|
||||
|
||||
let sponsorTimes = Config.config.segmentTimes.get(currentVideoID);
|
||||
const sponsorTimes = Config.config.segmentTimes.get(currentVideoID);
|
||||
|
||||
if (sponsorTimes != undefined && sponsorTimes.length > 0) {
|
||||
let confirmMessage = chrome.i18n.getMessage("clearThis") + getSegmentsMessage(sponsorTimes)
|
||||
const confirmMessage = chrome.i18n.getMessage("clearThis") + getSegmentsMessage(sponsorTimes)
|
||||
+ "\n" + chrome.i18n.getMessage("confirmMSG")
|
||||
if(!confirm(confirmMessage)) return;
|
||||
|
||||
|
@ -1377,7 +1377,7 @@ function vote(type: number, UUID: string, category?: string, skipNotice?: SkipNo
|
|||
skipNotice.setNoticeInfoMessage.bind(skipNotice)();
|
||||
}
|
||||
|
||||
let sponsorIndex = utils.getSponsorIndexFromUUID(sponsorTimes, UUID);
|
||||
const sponsorIndex = utils.getSponsorIndexFromUUID(sponsorTimes, UUID);
|
||||
|
||||
// Don't vote for preview sponsors
|
||||
if (sponsorIndex == -1 || sponsorTimes[sponsorIndex].UUID === null) return;
|
||||
|
@ -1420,7 +1420,7 @@ function vote(type: number, UUID: string, category?: string, skipNotice?: SkipNo
|
|||
|
||||
//Closes all notices that tell the user that a sponsor was just skipped
|
||||
function closeAllSkipNotices(){
|
||||
let notices = document.getElementsByClassName("sponsorSkipNotice");
|
||||
const notices = document.getElementsByClassName("sponsorSkipNotice");
|
||||
for (let i = 0; i < notices.length; i++) {
|
||||
notices[i].remove();
|
||||
}
|
||||
|
@ -1456,7 +1456,7 @@ function submitSponsorTimes() {
|
|||
//it can't update to this info yet
|
||||
closeInfoMenu();
|
||||
|
||||
let currentVideoID = sponsorVideoID;
|
||||
const currentVideoID = sponsorVideoID;
|
||||
|
||||
if (sponsorTimesSubmitting !== undefined && sponsorTimesSubmitting.length > 0) {
|
||||
submissionNotice = new SubmissionNotice(skipNoticeContentContainer, sendSubmitMessage);
|
||||
|
@ -1466,7 +1466,7 @@ function submitSponsorTimes() {
|
|||
|
||||
//send the message to the background js
|
||||
//called after all the checks have been made that it's okay to do so
|
||||
async function sendSubmitMessage(){
|
||||
async function sendSubmitMessage(): Promise<void> {
|
||||
//add loading animation
|
||||
(<HTMLImageElement> document.getElementById("submitImage")).src = chrome.extension.getURL("icons/PlayerUploadIconSponsorBlocker256px.png");
|
||||
document.getElementById("submitButton").style.animation = "rotate 1s 0s infinite";
|
||||
|
@ -1485,7 +1485,7 @@ async function sendSubmitMessage(){
|
|||
if (Config.config.minDuration > 0) {
|
||||
for (let i = 0; i < sponsorTimesSubmitting.length; i++) {
|
||||
if (sponsorTimesSubmitting[i].segment[1] - sponsorTimesSubmitting[i].segment[0] < Config.config.minDuration) {
|
||||
let confirmShort = chrome.i18n.getMessage("shortCheck") + "\n\n" +
|
||||
const confirmShort = chrome.i18n.getMessage("shortCheck") + "\n\n" +
|
||||
getSegmentsMessage(sponsorTimesSubmitting);
|
||||
|
||||
if(!confirm(confirmShort)) return;
|
||||
|
@ -1493,7 +1493,7 @@ async function sendSubmitMessage(){
|
|||
}
|
||||
}
|
||||
|
||||
let response = await utils.asyncRequestToServer("POST", "/api/skipSegments", {
|
||||
const response = await utils.asyncRequestToServer("POST", "/api/skipSegments", {
|
||||
videoID: sponsorVideoID,
|
||||
userID: Config.config.userID,
|
||||
segments: sponsorTimesSubmitting
|
||||
|
@ -1501,11 +1501,11 @@ async function sendSubmitMessage(){
|
|||
|
||||
if (response.status === 200) {
|
||||
//hide loading message
|
||||
let submitButton = document.getElementById("submitButton");
|
||||
const submitButton = document.getElementById("submitButton");
|
||||
submitButton.style.animation = "rotate 1s";
|
||||
//finish this animation
|
||||
//when the animation is over, hide the button
|
||||
let animationEndListener = function() {
|
||||
const animationEndListener = function() {
|
||||
changeStartSponsorButton(true, false);
|
||||
|
||||
submitButton.style.animation = "none";
|
||||
|
@ -1579,10 +1579,10 @@ function isUnlisted(): boolean {
|
|||
function addCSS() {
|
||||
if (!utils.isFirefox() && Config.config.invidiousInstances.includes(new URL(document.URL).host)) {
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
let head = document.getElementsByTagName("head")[0];
|
||||
const head = document.getElementsByTagName("head")[0];
|
||||
|
||||
for (const file of utils.css) {
|
||||
let fileref = document.createElement("link");
|
||||
const fileref = document.createElement("link");
|
||||
|
||||
fileref.rel = "stylesheet";
|
||||
fileref.type = "text/css";
|
||||
|
@ -1595,7 +1595,7 @@ function addCSS() {
|
|||
}
|
||||
|
||||
function sendRequestToCustomServer(type, fullAddress, callback) {
|
||||
let xmlhttp = new XMLHttpRequest();
|
||||
const xmlhttp = new XMLHttpRequest();
|
||||
|
||||
xmlhttp.open(type, fullAddress, true);
|
||||
|
||||
|
@ -1617,7 +1617,7 @@ function sendRequestToCustomServer(type, fullAddress, callback) {
|
|||
* Update the isAdPlaying flag and hide preview bar/controls if ad is playing
|
||||
*/
|
||||
function updateAdFlag() {
|
||||
let wasAdPlaying = isAdPlaying;
|
||||
const wasAdPlaying = isAdPlaying;
|
||||
isAdPlaying = document.getElementsByClassName('ad-showing').length > 0;
|
||||
|
||||
if(wasAdPlaying != isAdPlaying) {
|
||||
|
@ -1641,10 +1641,10 @@ function showTimeWithoutSkips(allSponsorTimes): void {
|
|||
}
|
||||
|
||||
// YouTube player time display
|
||||
let display = document.getElementsByClassName("ytp-time-display notranslate")[0];
|
||||
const display = document.getElementsByClassName("ytp-time-display notranslate")[0];
|
||||
if (!display) return;
|
||||
|
||||
let formatedTime = utils.getFormattedTime(video.duration - skipDuration);
|
||||
const formatedTime = utils.getFormattedTime(video.duration - skipDuration);
|
||||
|
||||
const durationID = "sponsorBlockDurationAfterSkips";
|
||||
let duration = document.getElementById(durationID);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import Config from "../config";
|
||||
import Utils from "../utils";
|
||||
let utils = new Utils();
|
||||
const utils = new Utils();
|
||||
|
||||
class PreviewBar {
|
||||
container: HTMLUListElement;
|
||||
|
@ -16,9 +16,9 @@ class PreviewBar {
|
|||
onInvidious: boolean;
|
||||
|
||||
timestamps: number[][];
|
||||
types: string;
|
||||
types: string[];
|
||||
|
||||
constructor(parent, onMobileYouTube, onInvidious) {
|
||||
constructor(parent: any, onMobileYouTube: boolean, onInvidious: boolean) {
|
||||
this.container = document.createElement('ul');
|
||||
this.container.id = 'previewbar';
|
||||
this.parent = parent;
|
||||
|
@ -31,15 +31,15 @@ class PreviewBar {
|
|||
this.setupHoverText();
|
||||
}
|
||||
|
||||
setupHoverText() {
|
||||
setupHoverText(): void {
|
||||
if (this.onMobileYouTube || this.onInvidious) return;
|
||||
|
||||
let seekBar = document.querySelector(".ytp-progress-bar-container");
|
||||
const seekBar = document.querySelector(".ytp-progress-bar-container");
|
||||
|
||||
// Create label placeholder
|
||||
let tooltipTextWrapper = document.querySelector(".ytp-tooltip-text-wrapper");
|
||||
let titleTooltip = document.querySelector(".ytp-tooltip-title");
|
||||
let categoryTooltip = document.createElement("div");
|
||||
const tooltipTextWrapper = document.querySelector(".ytp-tooltip-text-wrapper");
|
||||
const titleTooltip = document.querySelector(".ytp-tooltip-title");
|
||||
const categoryTooltip = document.createElement("div");
|
||||
categoryTooltip.className = "sbHidden ytp-tooltip-title";
|
||||
categoryTooltip.id = "sponsor-block-category-tooltip"
|
||||
|
||||
|
@ -64,12 +64,12 @@ class PreviewBar {
|
|||
return;
|
||||
}
|
||||
|
||||
let tooltips = document.querySelectorAll(".ytp-tooltip-text");
|
||||
const tooltips = document.querySelectorAll(".ytp-tooltip-text");
|
||||
for (const tooltip of tooltips) {
|
||||
let splitData = tooltip.textContent.split(":");
|
||||
const splitData = tooltip.textContent.split(":");
|
||||
if (splitData.length === 2 && !isNaN(parseInt(splitData[0])) && !isNaN(parseInt(splitData[1]))) {
|
||||
// Add label
|
||||
let timeInSeconds = parseInt(splitData[0]) * 60 + parseInt(splitData[1]);
|
||||
const timeInSeconds = parseInt(splitData[0]) * 60 + parseInt(splitData[1]);
|
||||
|
||||
// Find category at that location
|
||||
let category = null;
|
||||
|
@ -112,7 +112,7 @@ class PreviewBar {
|
|||
});
|
||||
}
|
||||
|
||||
updatePosition(parent) {
|
||||
updatePosition(parent: any): void {
|
||||
//below the seek bar
|
||||
// this.parent.insertAdjacentElement("afterEnd", this.container);
|
||||
|
||||
|
@ -129,15 +129,15 @@ class PreviewBar {
|
|||
this.parent.insertAdjacentElement("afterBegin", this.container);
|
||||
}
|
||||
|
||||
updateColor(segment, color, opacity) {
|
||||
let bars = <NodeListOf<HTMLElement>> document.querySelectorAll('[data-vs-segment-type=' + segment + ']');
|
||||
for (let bar of bars) {
|
||||
updateColor(segment: string, color: string, opacity: string): void {
|
||||
const bars = <NodeListOf<HTMLElement>> document.querySelectorAll('[data-vs-segment-type=' + segment + ']');
|
||||
for (const bar of bars) {
|
||||
bar.style.backgroundColor = color;
|
||||
bar.style.opacity = opacity;
|
||||
}
|
||||
}
|
||||
|
||||
set(timestamps, types, duration) {
|
||||
set(timestamps: number[][], types: string[], duration: number): void {
|
||||
while (this.container.firstChild) {
|
||||
this.container.removeChild(this.container.firstChild);
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ class PreviewBar {
|
|||
width = (timestamps[i][1] - timestamps[i][0]) / duration * 100;
|
||||
width = Math.floor(width * 100) / 100;
|
||||
|
||||
let bar = this.createBar();
|
||||
const bar = this.createBar();
|
||||
bar.setAttribute('data-vs-segment-type', types[i]);
|
||||
|
||||
bar.style.backgroundColor = Config.config.barTypes[types[i]].color;
|
||||
|
@ -171,14 +171,14 @@ class PreviewBar {
|
|||
}
|
||||
}
|
||||
|
||||
createBar() {
|
||||
let bar = document.createElement('li');
|
||||
createBar(): HTMLLIElement {
|
||||
const bar = document.createElement('li');
|
||||
bar.classList.add('previewbar');
|
||||
bar.innerHTML = ' ';
|
||||
return bar;
|
||||
}
|
||||
|
||||
remove() {
|
||||
remove(): void {
|
||||
this.container.remove();
|
||||
this.container = undefined;
|
||||
}
|
||||
|
|
136
src/options.ts
136
src/options.ts
|
@ -6,7 +6,7 @@ import * as CompileConfig from "../config.json";
|
|||
|
||||
import Utils from "./utils";
|
||||
import CategoryChooser from "./render/CategoryChooser";
|
||||
var utils = new Utils();
|
||||
const utils = new Utils();
|
||||
|
||||
window.addEventListener('DOMContentLoaded', init);
|
||||
|
||||
|
@ -27,19 +27,19 @@ async function init() {
|
|||
await utils.wait(() => Config.config !== null);
|
||||
|
||||
// Set all of the toggle options to the correct option
|
||||
let optionsContainer = document.getElementById("options");
|
||||
let optionsElements = optionsContainer.querySelectorAll("*");
|
||||
const optionsContainer = document.getElementById("options");
|
||||
const optionsElements = optionsContainer.querySelectorAll("*");
|
||||
|
||||
for (let i = 0; i < optionsElements.length; i++) {
|
||||
switch (optionsElements[i].getAttribute("option-type")) {
|
||||
case "toggle":
|
||||
let option = optionsElements[i].getAttribute("sync-option");
|
||||
let optionResult = Config.config[option];
|
||||
case "toggle": {
|
||||
const option = optionsElements[i].getAttribute("sync-option");
|
||||
const optionResult = Config.config[option];
|
||||
|
||||
let checkbox = optionsElements[i].querySelector("input");
|
||||
let reverse = optionsElements[i].getAttribute("toggle-type") === "reverse";
|
||||
const checkbox = optionsElements[i].querySelector("input");
|
||||
const reverse = optionsElements[i].getAttribute("toggle-type") === "reverse";
|
||||
|
||||
let confirmMessage = optionsElements[i].getAttribute("confirm-message");
|
||||
const confirmMessage = optionsElements[i].getAttribute("confirm-message");
|
||||
|
||||
if (optionResult != undefined) {
|
||||
checkbox.checked = optionResult;
|
||||
|
@ -76,7 +76,7 @@ async function init() {
|
|||
// Enable the notice
|
||||
Config.config["dontShowNotice"] = false;
|
||||
|
||||
let showNoticeSwitch = <HTMLInputElement> document.querySelector("[sync-option='dontShowNotice'] > label > label > input");
|
||||
const showNoticeSwitch = <HTMLInputElement> document.querySelector("[sync-option='dontShowNotice'] > label > label > input");
|
||||
showNoticeSwitch.checked = true;
|
||||
}
|
||||
|
||||
|
@ -84,19 +84,20 @@ async function init() {
|
|||
}
|
||||
});
|
||||
break;
|
||||
case "text-change":
|
||||
let textChangeOption = optionsElements[i].getAttribute("sync-option");
|
||||
let textChangeInput = <HTMLInputElement> optionsElements[i].querySelector(".option-text-box");
|
||||
}
|
||||
case "text-change": {
|
||||
const textChangeOption = optionsElements[i].getAttribute("sync-option");
|
||||
const textChangeInput = <HTMLInputElement> optionsElements[i].querySelector(".option-text-box");
|
||||
|
||||
let textChangeSetButton = <HTMLElement> optionsElements[i].querySelector(".text-change-set");
|
||||
const textChangeSetButton = <HTMLElement> optionsElements[i].querySelector(".text-change-set");
|
||||
|
||||
textChangeInput.value = Config.config[textChangeOption];
|
||||
|
||||
textChangeSetButton.addEventListener("click", async () => {
|
||||
// See if anything extra must be done
|
||||
switch (textChangeOption) {
|
||||
case "serverAddress":
|
||||
let result = validateServerAddress(textChangeInput.value);
|
||||
case "serverAddress": {
|
||||
const result = validateServerAddress(textChangeInput.value);
|
||||
|
||||
if (result !== null) {
|
||||
textChangeInput.value = result;
|
||||
|
@ -106,7 +107,7 @@ async function init() {
|
|||
|
||||
// Permission needed on Firefox
|
||||
if (utils.isFirefox()) {
|
||||
let permissionSuccess = await new Promise((resolve, reject) => {
|
||||
const permissionSuccess = await new Promise((resolve, reject) => {
|
||||
chrome.permissions.request({
|
||||
origins: [textChangeInput.value + "/"],
|
||||
permissions: []
|
||||
|
@ -118,12 +119,13 @@ async function init() {
|
|||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Config.config[textChangeOption] = textChangeInput.value;
|
||||
});
|
||||
|
||||
// Reset to the default if needed
|
||||
let textChangeResetButton = <HTMLElement> optionsElements[i].querySelector(".text-change-reset");
|
||||
const textChangeResetButton = <HTMLElement> optionsElements[i].querySelector(".text-change-reset");
|
||||
textChangeResetButton.addEventListener("click", () => {
|
||||
if (!confirm(chrome.i18n.getMessage("areYouSureReset"))) return;
|
||||
|
||||
|
@ -133,11 +135,12 @@ async function init() {
|
|||
});
|
||||
|
||||
break;
|
||||
case "private-text-change":
|
||||
let button = optionsElements[i].querySelector(".trigger-button");
|
||||
}
|
||||
case "private-text-change": {
|
||||
const button = optionsElements[i].querySelector(".trigger-button");
|
||||
button.addEventListener("click", () => activatePrivateTextChange(<HTMLElement> optionsElements[i]));
|
||||
|
||||
let privateTextChangeOption = optionsElements[i].getAttribute("sync-option");
|
||||
const privateTextChangeOption = optionsElements[i].getAttribute("sync-option");
|
||||
// See if anything extra must be done
|
||||
switch (privateTextChangeOption) {
|
||||
case "invidiousInstances":
|
||||
|
@ -145,8 +148,9 @@ async function init() {
|
|||
}
|
||||
|
||||
break;
|
||||
case "button-press":
|
||||
let actionButton = optionsElements[i].querySelector(".trigger-button");
|
||||
}
|
||||
case "button-press": {
|
||||
const actionButton = optionsElements[i].querySelector(".trigger-button");
|
||||
|
||||
switch(optionsElements[i].getAttribute("sync-option")) {
|
||||
case "copyDebugInformation":
|
||||
|
@ -155,19 +159,21 @@ async function init() {
|
|||
}
|
||||
|
||||
break;
|
||||
case "keybind-change":
|
||||
let keybindButton = optionsElements[i].querySelector(".trigger-button");
|
||||
}
|
||||
case "keybind-change": {
|
||||
const keybindButton = optionsElements[i].querySelector(".trigger-button");
|
||||
keybindButton.addEventListener("click", () => activateKeybindChange(<HTMLElement> optionsElements[i]));
|
||||
|
||||
break;
|
||||
case "display":
|
||||
}
|
||||
case "display":{
|
||||
updateDisplayElement(<HTMLElement> optionsElements[i])
|
||||
|
||||
break;
|
||||
case "number-change":
|
||||
let numberChangeOption = optionsElements[i].getAttribute("sync-option");
|
||||
let configValue = Config.config[numberChangeOption];
|
||||
let numberInput = optionsElements[i].querySelector("input");
|
||||
}
|
||||
case "number-change": {
|
||||
const numberChangeOption = optionsElements[i].getAttribute("sync-option");
|
||||
const configValue = Config.config[numberChangeOption];
|
||||
const numberInput = optionsElements[i].querySelector("input");
|
||||
|
||||
if (isNaN(configValue) || configValue < 0) {
|
||||
numberInput.value = Config.defaults[numberChangeOption];
|
||||
|
@ -180,6 +186,7 @@ async function init() {
|
|||
});
|
||||
|
||||
break;
|
||||
}
|
||||
case "react-CategoryChooserComponent":
|
||||
new CategoryChooser(optionsElements[i]);
|
||||
break;
|
||||
|
@ -196,8 +203,8 @@ async function init() {
|
|||
* @param {String} element
|
||||
*/
|
||||
function optionsConfigUpdateListener(changes) {
|
||||
let optionsContainer = document.getElementById("options");
|
||||
let optionsElements = optionsContainer.querySelectorAll("*");
|
||||
const optionsContainer = document.getElementById("options");
|
||||
const optionsElements = optionsContainer.querySelectorAll("*");
|
||||
|
||||
for (let i = 0; i < optionsElements.length; i++) {
|
||||
switch (optionsElements[i].getAttribute("option-type")) {
|
||||
|
@ -213,8 +220,8 @@ function optionsConfigUpdateListener(changes) {
|
|||
* @param element
|
||||
*/
|
||||
function updateDisplayElement(element: HTMLElement) {
|
||||
let displayOption = element.getAttribute("sync-option")
|
||||
let displayText = Config.config[displayOption];
|
||||
const displayOption = element.getAttribute("sync-option")
|
||||
const displayText = Config.config[displayOption];
|
||||
element.innerText = displayText;
|
||||
|
||||
// See if anything extra must be run
|
||||
|
@ -232,10 +239,10 @@ function updateDisplayElement(element: HTMLElement) {
|
|||
* @param option
|
||||
*/
|
||||
function invidiousInstanceAddInit(element: HTMLElement, option: string) {
|
||||
let textBox = <HTMLInputElement> element.querySelector(".option-text-box");
|
||||
let button = element.querySelector(".trigger-button");
|
||||
const textBox = <HTMLInputElement> element.querySelector(".option-text-box");
|
||||
const button = element.querySelector(".trigger-button");
|
||||
|
||||
let setButton = element.querySelector(".text-change-set");
|
||||
const setButton = element.querySelector(".text-change-set");
|
||||
setButton.addEventListener("click", async function(e) {
|
||||
if (textBox.value == "" || textBox.value.includes("/") || textBox.value.includes("http")) {
|
||||
alert(chrome.i18n.getMessage("addInvidiousInstanceError"));
|
||||
|
@ -248,7 +255,7 @@ function invidiousInstanceAddInit(element: HTMLElement, option: string) {
|
|||
|
||||
Config.config[option] = instanceList;
|
||||
|
||||
let checkbox = <HTMLInputElement> document.querySelector("#support-invidious input");
|
||||
const checkbox = <HTMLInputElement> document.querySelector("#support-invidious input");
|
||||
checkbox.checked = true;
|
||||
|
||||
invidiousOnClick(checkbox, "supportInvidious");
|
||||
|
@ -261,7 +268,7 @@ function invidiousInstanceAddInit(element: HTMLElement, option: string) {
|
|||
}
|
||||
});
|
||||
|
||||
let resetButton = element.querySelector(".invidious-instance-reset");
|
||||
const resetButton = element.querySelector(".invidious-instance-reset");
|
||||
resetButton.addEventListener("click", function(e) {
|
||||
if (confirm(chrome.i18n.getMessage("resetInvidiousInstanceAlert"))) {
|
||||
// Set to a clone of the default
|
||||
|
@ -298,7 +305,7 @@ function invidiousInit(checkbox: HTMLInputElement, option: string) {
|
|||
* @param checkbox
|
||||
* @param option
|
||||
*/
|
||||
async function invidiousOnClick(checkbox: HTMLInputElement, option: string) {
|
||||
async function invidiousOnClick(checkbox: HTMLInputElement, option: string): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
if (checkbox.checked) {
|
||||
utils.setupExtraSitePermissions(function (granted) {
|
||||
|
@ -323,20 +330,20 @@ async function invidiousOnClick(checkbox: HTMLInputElement, option: string) {
|
|||
* @param element
|
||||
*/
|
||||
function activateKeybindChange(element: HTMLElement) {
|
||||
let button = element.querySelector(".trigger-button");
|
||||
const button = element.querySelector(".trigger-button");
|
||||
if (button.classList.contains("disabled")) return;
|
||||
|
||||
button.classList.add("disabled");
|
||||
|
||||
let option = element.getAttribute("sync-option");
|
||||
const option = element.getAttribute("sync-option");
|
||||
|
||||
let currentlySet = Config.config[option] !== null ? chrome.i18n.getMessage("keybindCurrentlySet") : "";
|
||||
const currentlySet = Config.config[option] !== null ? chrome.i18n.getMessage("keybindCurrentlySet") : "";
|
||||
|
||||
let status = <HTMLElement> element.querySelector(".option-hidden-section > .keybind-status");
|
||||
const status = <HTMLElement> element.querySelector(".option-hidden-section > .keybind-status");
|
||||
status.innerText = chrome.i18n.getMessage("keybindDescription") + currentlySet;
|
||||
|
||||
if (Config.config[option] !== null) {
|
||||
let statusKey = <HTMLElement> element.querySelector(".option-hidden-section > .keybind-status-key");
|
||||
const statusKey = <HTMLElement> element.querySelector(".option-hidden-section > .keybind-status-key");
|
||||
statusKey.innerText = Config.config[option];
|
||||
}
|
||||
|
||||
|
@ -352,19 +359,19 @@ function activateKeybindChange(element: HTMLElement) {
|
|||
* @param e
|
||||
*/
|
||||
function keybindKeyPressed(element: HTMLElement, e: KeyboardEvent) {
|
||||
var key = e.key;
|
||||
const key = e.key;
|
||||
|
||||
if (["Shift", "Control", "Meta", "Alt", "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Tab"].indexOf(key) !== -1) {
|
||||
|
||||
// Wait for more
|
||||
document.addEventListener("keydown", (e) => keybindKeyPressed(element, e), {once: true});
|
||||
} else {
|
||||
let button: HTMLElement = element.querySelector(".trigger-button");
|
||||
let option = element.getAttribute("sync-option");
|
||||
const button: HTMLElement = element.querySelector(".trigger-button");
|
||||
const option = element.getAttribute("sync-option");
|
||||
|
||||
// Make sure keybind isn't used by the other listener
|
||||
// TODO: If other keybindings are going to be added, we need a better way to find the other keys used.
|
||||
let otherKeybind = (option === "startSponsorKeybind") ? Config.config['submitKeybind'] : Config.config['startSponsorKeybind'];
|
||||
const otherKeybind = (option === "startSponsorKeybind") ? Config.config['submitKeybind'] : Config.config['startSponsorKeybind'];
|
||||
if (key === otherKeybind) {
|
||||
closeKeybindOption(element, button);
|
||||
|
||||
|
@ -381,10 +388,10 @@ function keybindKeyPressed(element: HTMLElement, e: KeyboardEvent) {
|
|||
|
||||
Config.config[option] = key;
|
||||
|
||||
let status = <HTMLElement> element.querySelector(".option-hidden-section > .keybind-status");
|
||||
const status = <HTMLElement> element.querySelector(".option-hidden-section > .keybind-status");
|
||||
status.innerText = chrome.i18n.getMessage("keybindDescriptionComplete");
|
||||
|
||||
let statusKey = <HTMLElement> element.querySelector(".option-hidden-section > .keybind-status-key");
|
||||
const statusKey = <HTMLElement> element.querySelector(".option-hidden-section > .keybind-status-key");
|
||||
statusKey.innerText = key;
|
||||
|
||||
button.classList.remove("disabled");
|
||||
|
@ -408,13 +415,13 @@ function closeKeybindOption(element: HTMLElement, button: HTMLElement) {
|
|||
* @param element
|
||||
*/
|
||||
function activatePrivateTextChange(element: HTMLElement) {
|
||||
let button = element.querySelector(".trigger-button");
|
||||
const button = element.querySelector(".trigger-button");
|
||||
if (button.classList.contains("disabled")) return;
|
||||
|
||||
button.classList.add("disabled");
|
||||
|
||||
let textBox = <HTMLInputElement> element.querySelector(".option-text-box");
|
||||
let option = element.getAttribute("sync-option");
|
||||
const textBox = <HTMLInputElement> element.querySelector(".option-text-box");
|
||||
const option = element.getAttribute("sync-option");
|
||||
|
||||
// See if anything extra must be done
|
||||
switch (option) {
|
||||
|
@ -427,8 +434,8 @@ function activatePrivateTextChange(element: HTMLElement) {
|
|||
|
||||
// See if anything extra must be done
|
||||
switch (option) {
|
||||
case "*":
|
||||
let jsonData = JSON.parse(JSON.stringify(Config.localConfig));
|
||||
case "*": {
|
||||
const jsonData = JSON.parse(JSON.stringify(Config.localConfig));
|
||||
|
||||
// Fix segmentTimes data as it is destroyed from the JSON stringify
|
||||
jsonData.segmentTimes = Config.encodeStoredItem(Config.localConfig.segmentTimes);
|
||||
|
@ -436,12 +443,13 @@ function activatePrivateTextChange(element: HTMLElement) {
|
|||
result = JSON.stringify(jsonData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
textBox.value = result;
|
||||
|
||||
let setButton = element.querySelector(".text-change-set");
|
||||
const setButton = element.querySelector(".text-change-set");
|
||||
setButton.addEventListener("click", async () => {
|
||||
let confirmMessage = element.getAttribute("confirm-message");
|
||||
const confirmMessage = element.getAttribute("confirm-message");
|
||||
|
||||
if (confirmMessage === null || confirm(chrome.i18n.getMessage(confirmMessage))) {
|
||||
|
||||
|
@ -449,14 +457,14 @@ function activatePrivateTextChange(element: HTMLElement) {
|
|||
switch (option) {
|
||||
case "*":
|
||||
try {
|
||||
let newConfig = JSON.parse(textBox.value);
|
||||
const newConfig = JSON.parse(textBox.value);
|
||||
for (const key in newConfig) {
|
||||
Config.config[key] = newConfig[key];
|
||||
}
|
||||
Config.convertJSON();
|
||||
|
||||
if (newConfig.supportInvidious) {
|
||||
let checkbox = <HTMLInputElement> document.querySelector("#support-invidious > label > label > input");
|
||||
const checkbox = <HTMLInputElement> document.querySelector("#support-invidious > label > label > input");
|
||||
|
||||
checkbox.checked = true;
|
||||
await invidiousOnClick(checkbox, "supportInvidious");
|
||||
|
@ -503,7 +511,7 @@ function validateServerAddress(input: string): string {
|
|||
|
||||
function copyDebugOutputToClipboard() {
|
||||
// Build output debug information object
|
||||
let output = {
|
||||
const output = {
|
||||
debug: {
|
||||
userAgent: navigator.userAgent,
|
||||
platform: navigator.platform,
|
||||
|
@ -528,7 +536,7 @@ function copyDebugOutputToClipboard() {
|
|||
.then(() => {
|
||||
alert(chrome.i18n.getMessage("copyDebugInformationComplete"));
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
alert(chrome.i18n.getMessage("copyDebugInformationFailed"));
|
||||
});;
|
||||
});
|
||||
}
|
70
src/popup.ts
70
src/popup.ts
|
@ -2,7 +2,7 @@ import Config from "./config";
|
|||
|
||||
import Utils from "./utils";
|
||||
import { SponsorTime, SponsorHideType } from "./types";
|
||||
var utils = new Utils();
|
||||
const utils = new Utils();
|
||||
|
||||
interface MessageListener {
|
||||
(request: any, sender: any, callback: (response: any) => void): void;
|
||||
|
@ -38,14 +38,14 @@ class MessageHandler {
|
|||
}
|
||||
|
||||
//make this a function to allow this to run on the content page
|
||||
async function runThePopup(messageListener?: MessageListener) {
|
||||
var messageHandler = new MessageHandler(messageListener);
|
||||
async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
||||
const messageHandler = new MessageHandler(messageListener);
|
||||
|
||||
utils.localizeHtmlPage();
|
||||
|
||||
await utils.wait(() => Config.config !== null);
|
||||
|
||||
var PageElements: any = {};
|
||||
const PageElements: any = {};
|
||||
|
||||
[
|
||||
"sponsorblockPopup",
|
||||
|
@ -126,7 +126,7 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||
let currentVideoID = null;
|
||||
|
||||
//show proper disable skipping button
|
||||
let disableSkipping = Config.config.disableSkipping;
|
||||
const disableSkipping = Config.config.disableSkipping;
|
||||
if (disableSkipping != undefined && disableSkipping) {
|
||||
PageElements.disableSkipping.style.display = "none";
|
||||
PageElements.enableSkipping.style.display = "unset";
|
||||
|
@ -135,7 +135,7 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||
|
||||
//if the don't show notice again variable is true, an option to
|
||||
// disable should be available
|
||||
let dontShowNotice = Config.config.dontShowNotice;
|
||||
const dontShowNotice = Config.config.dontShowNotice;
|
||||
if (dontShowNotice != undefined && dontShowNotice) {
|
||||
PageElements.showNoticeAgain.style.display = "unset";
|
||||
}
|
||||
|
@ -152,13 +152,13 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||
PageElements.sponsorTimesContributionsContainer.classList.remove("hidden");
|
||||
|
||||
//get the userID
|
||||
let userID = Config.config.userID;
|
||||
const userID = Config.config.userID;
|
||||
if (userID != undefined) {
|
||||
//there are probably some views on these submissions then
|
||||
//get the amount of views from the sponsors submitted
|
||||
utils.sendRequestToServer("GET", "/api/getViewsForUser?userID=" + userID, function(response) {
|
||||
if (response.status == 200) {
|
||||
let viewCount = JSON.parse(response.responseText).viewCount;
|
||||
const viewCount = JSON.parse(response.responseText).viewCount;
|
||||
if (viewCount != 0) {
|
||||
if (viewCount > 1) {
|
||||
PageElements.sponsorTimesViewsDisplayEndWord.innerText = chrome.i18n.getMessage("Segments");
|
||||
|
@ -175,7 +175,7 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||
//get this time in minutes
|
||||
utils.sendRequestToServer("GET", "/api/getSavedTimeForUser?userID=" + userID, function(response) {
|
||||
if (response.status == 200) {
|
||||
let minutesSaved = JSON.parse(response.responseText).timeSaved;
|
||||
const minutesSaved = JSON.parse(response.responseText).timeSaved;
|
||||
if (minutesSaved != 0) {
|
||||
if (minutesSaved != 1) {
|
||||
PageElements.sponsorTimesOthersTimeSavedEndWord.innerText = chrome.i18n.getMessage("minsLower");
|
||||
|
@ -227,7 +227,7 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||
currentVideoID = result.videoID;
|
||||
loadTabData(tabs);
|
||||
} else if (result == undefined && chrome.runtime.lastError) {
|
||||
//this isn't a YouTube video then, or at least the content script is not loaded
|
||||
// this isn't a YouTube video then, or at least the content script is not loaded
|
||||
displayNoVideo();
|
||||
}
|
||||
});
|
||||
|
@ -241,7 +241,7 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||
}
|
||||
|
||||
//load video times for this video
|
||||
let sponsorTimesStorage = Config.config.segmentTimes.get(currentVideoID);
|
||||
const sponsorTimesStorage = Config.config.segmentTimes.get(currentVideoID);
|
||||
if (sponsorTimesStorage != undefined && sponsorTimesStorage.length > 0) {
|
||||
if (sponsorTimesStorage[sponsorTimesStorage.length - 1] != undefined && sponsorTimesStorage[sponsorTimesStorage.length - 1].segment.length < 2) {
|
||||
startTimeChosen = true;
|
||||
|
@ -322,7 +322,7 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||
}
|
||||
|
||||
function startSponsorCallback(response) {
|
||||
let sponsorTimesIndex = sponsorTimes.length - (startTimeChosen ? 1 : 0);
|
||||
const sponsorTimesIndex = sponsorTimes.length - (startTimeChosen ? 1 : 0);
|
||||
|
||||
if (sponsorTimes[sponsorTimesIndex] == undefined) {
|
||||
sponsorTimes[sponsorTimesIndex] = {
|
||||
|
@ -334,7 +334,7 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||
|
||||
sponsorTimes[sponsorTimesIndex].segment[startTimeChosen ? 1 : 0] = response.time;
|
||||
|
||||
let localStartTimeChosen = startTimeChosen;
|
||||
const localStartTimeChosen = startTimeChosen;
|
||||
Config.config.segmentTimes.set(currentVideoID, sponsorTimes);
|
||||
|
||||
//send a message to the client script
|
||||
|
@ -363,19 +363,19 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||
if (request.sponsorTimes != undefined) {
|
||||
|
||||
// Sort list by start time
|
||||
let segmentTimes = request.sponsorTimes
|
||||
const segmentTimes = request.sponsorTimes
|
||||
.sort((a, b) => a.segment[1] - b.segment[1])
|
||||
.sort((a, b) => a.segment[0] - b.segment[0]);
|
||||
|
||||
//add them as buttons to the issue reporting container
|
||||
let container = document.getElementById("issueReporterTimeButtons");
|
||||
const container = document.getElementById("issueReporterTimeButtons");
|
||||
for (let i = 0; i < segmentTimes.length; i++) {
|
||||
let UUID = segmentTimes[i].UUID;
|
||||
const UUID = segmentTimes[i].UUID;
|
||||
|
||||
let sponsorTimeButton = document.createElement("button");
|
||||
const sponsorTimeButton = document.createElement("button");
|
||||
sponsorTimeButton.className = "segmentTimeButton popupElement";
|
||||
|
||||
let prefix = chrome.i18n.getMessage("category_" + segmentTimes[i].category) + ": ";
|
||||
const prefix = chrome.i18n.getMessage("category_" + segmentTimes[i].category) + ": ";
|
||||
|
||||
let extraInfo = "";
|
||||
if (segmentTimes[i].hidden === SponsorHideType.Downvoted) {
|
||||
|
@ -388,28 +388,28 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||
|
||||
sponsorTimeButton.innerText = prefix + getFormattedTime(segmentTimes[i].segment[0]) + " " + chrome.i18n.getMessage("to") + " " + getFormattedTime(segmentTimes[i].segment[1]) + extraInfo;
|
||||
|
||||
let categoryColorCircle = document.createElement("span");
|
||||
const categoryColorCircle = document.createElement("span");
|
||||
categoryColorCircle.id = "sponsorTimesCategoryColorCircle" + UUID;
|
||||
categoryColorCircle.style.backgroundColor = Config.config.barTypes[segmentTimes[i].category].color;
|
||||
categoryColorCircle.classList.add("dot");
|
||||
categoryColorCircle.classList.add("sponsorTimesCategoryColorCircle");
|
||||
|
||||
let votingButtons = document.createElement("div");
|
||||
const votingButtons = document.createElement("div");
|
||||
votingButtons.classList.add("votingButtons");
|
||||
|
||||
//thumbs up and down buttons
|
||||
let voteButtonsContainer = document.createElement("div");
|
||||
const voteButtonsContainer = document.createElement("div");
|
||||
voteButtonsContainer.id = "sponsorTimesVoteButtonsContainer" + UUID;
|
||||
voteButtonsContainer.setAttribute("align", "center");
|
||||
voteButtonsContainer.style.display = "none"
|
||||
|
||||
let upvoteButton = document.createElement("img");
|
||||
const upvoteButton = document.createElement("img");
|
||||
upvoteButton.id = "sponsorTimesUpvoteButtonsContainer" + UUID;
|
||||
upvoteButton.className = "voteButton";
|
||||
upvoteButton.src = chrome.extension.getURL("icons/thumbs_up.svg");
|
||||
upvoteButton.addEventListener("click", () => vote(1, UUID));
|
||||
|
||||
let downvoteButton = document.createElement("img");
|
||||
const downvoteButton = document.createElement("img");
|
||||
downvoteButton.id = "sponsorTimesDownvoteButtonsContainer" + UUID;
|
||||
downvoteButton.className = "voteButton";
|
||||
downvoteButton.src = chrome.extension.getURL("icons/thumbs_down.svg");
|
||||
|
@ -425,12 +425,12 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||
});
|
||||
|
||||
// Will contain request status
|
||||
let voteStatusContainer = document.createElement("div");
|
||||
const voteStatusContainer = document.createElement("div");
|
||||
voteStatusContainer.id = "sponsorTimesVoteStatusContainer" + UUID;
|
||||
voteStatusContainer.classList.add("sponsorTimesVoteStatusContainer");
|
||||
voteStatusContainer.style.display = "none";
|
||||
|
||||
let thanksForVotingText = document.createElement("div");
|
||||
const thanksForVotingText = document.createElement("div");
|
||||
thanksForVotingText.id = "sponsorTimesThanksForVotingText" + UUID;
|
||||
thanksForVotingText.classList.add("sponsorTimesThanksForVotingText");
|
||||
voteStatusContainer.appendChild(thanksForVotingText);
|
||||
|
@ -553,13 +553,13 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||
}
|
||||
|
||||
function addVoteMessage(message, UUID) {
|
||||
let voteButtonsContainer = document.getElementById("sponsorTimesVoteButtonsContainer" + UUID);
|
||||
const voteButtonsContainer = document.getElementById("sponsorTimesVoteButtonsContainer" + UUID);
|
||||
voteButtonsContainer.style.display = "none";
|
||||
|
||||
let voteStatusContainer = document.getElementById("sponsorTimesVoteStatusContainer" + UUID);
|
||||
const voteStatusContainer = document.getElementById("sponsorTimesVoteStatusContainer" + UUID);
|
||||
voteStatusContainer.style.removeProperty("display");
|
||||
|
||||
let thanksForVotingText = document.getElementById("sponsorTimesThanksForVotingText" + UUID);
|
||||
const thanksForVotingText = document.getElementById("sponsorTimesThanksForVotingText" + UUID);
|
||||
thanksForVotingText.innerText = message;
|
||||
}
|
||||
|
||||
|
@ -587,15 +587,15 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||
|
||||
//converts time in seconds to minutes:seconds
|
||||
function getFormattedTime(seconds) {
|
||||
let minutes = Math.floor(seconds / 60);
|
||||
let secondsDisplayNumber = Math.round(seconds - minutes * 60);
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const secondsDisplayNumber = Math.round(seconds - minutes * 60);
|
||||
let secondsDisplay = String(secondsDisplayNumber);
|
||||
if (secondsDisplayNumber < 10) {
|
||||
//add a zero
|
||||
secondsDisplay = "0" + secondsDisplay;
|
||||
}
|
||||
|
||||
let formatted = minutes + ":" + secondsDisplay;
|
||||
const formatted = minutes + ":" + secondsDisplay;
|
||||
|
||||
return formatted;
|
||||
}
|
||||
|
@ -669,7 +669,7 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||
}
|
||||
|
||||
//remove this channel
|
||||
let index = whitelistedChannels.indexOf(response.channelID);
|
||||
const index = whitelistedChannels.indexOf(response.channelID);
|
||||
whitelistedChannels.splice(index, 1);
|
||||
|
||||
//change button
|
||||
|
@ -717,14 +717,14 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||
|
||||
//converts time in seconds to minutes
|
||||
function getTimeInMinutes(seconds) {
|
||||
let minutes = Math.floor(seconds / 60);
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
|
||||
return minutes;
|
||||
}
|
||||
|
||||
//converts time in seconds to seconds past the last minute
|
||||
function getTimeInFormattedSeconds(seconds) {
|
||||
let minutes = seconds % 60;
|
||||
const minutes = seconds % 60;
|
||||
let secondsFormatted = minutes.toFixed(3);
|
||||
|
||||
if (minutes < 10) {
|
||||
|
@ -742,7 +742,7 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||
* @returns {string}
|
||||
*/
|
||||
function getFormattedHours(minues) {
|
||||
let hours = Math.floor(minues / 60);
|
||||
const hours = Math.floor(minues / 60);
|
||||
return (hours > 0 ? hours + "h " : "") + (minues % 60).toFixed(1);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ class SkipNotice {
|
|||
|
||||
skipNoticeRef: React.MutableRefObject<SkipNoticeComponent>;
|
||||
|
||||
constructor(segments: SponsorTime[], autoSkip: boolean = false, contentContainer: ContentContainer) {
|
||||
constructor(segments: SponsorTime[], autoSkip = false, contentContainer: ContentContainer) {
|
||||
this.segments = segments;
|
||||
this.autoSkip = autoSkip;
|
||||
this.contentContainer = contentContainer;
|
||||
|
@ -24,7 +24,7 @@ class SkipNotice {
|
|||
|| document.getElementById("movie_player") || document.querySelector("#player-container .video-js");
|
||||
if (referenceNode == null) {
|
||||
//for embeds
|
||||
let player = document.getElementById("player");
|
||||
const player = document.getElementById("player");
|
||||
referenceNode = player.firstChild as HTMLElement;
|
||||
let index = 1;
|
||||
|
||||
|
@ -40,7 +40,7 @@ class SkipNotice {
|
|||
referenceNode = document.querySelector("#main-panel.ytmusic-player-page");
|
||||
}
|
||||
|
||||
let amountOfPreviousNotices = document.getElementsByClassName("sponsorSkipNotice").length;
|
||||
const amountOfPreviousNotices = document.getElementsByClassName("sponsorSkipNotice").length;
|
||||
//this is the suffix added at the end of every id
|
||||
let idSuffix = "";
|
||||
for (const segment of this.segments) {
|
||||
|
@ -63,7 +63,7 @@ class SkipNotice {
|
|||
);
|
||||
}
|
||||
|
||||
close() {
|
||||
close(): void {
|
||||
ReactDOM.unmountComponentAtNode(this.noticeElement);
|
||||
|
||||
this.noticeElement.remove();
|
||||
|
|
|
@ -2,18 +2,19 @@ import * as React from "react";
|
|||
import * as ReactDOM from "react-dom";
|
||||
|
||||
import SubmissionNoticeComponent from "../components/SubmissionNoticeComponent";
|
||||
import { ContentContainer } from "../types";
|
||||
|
||||
class SubmissionNotice {
|
||||
// Contains functions and variables from the content script needed by the skip notice
|
||||
contentContainer: () => any;
|
||||
contentContainer: () => unknown;
|
||||
|
||||
callback: () => any;
|
||||
callback: () => unknown;
|
||||
|
||||
noticeRef: React.MutableRefObject<SubmissionNoticeComponent>;
|
||||
|
||||
noticeElement: HTMLDivElement;
|
||||
|
||||
constructor(contentContainer: () => any, callback: () => any) {
|
||||
constructor(contentContainer: ContentContainer, callback: () => unknown) {
|
||||
this.noticeRef = React.createRef();
|
||||
|
||||
this.contentContainer = contentContainer;
|
||||
|
@ -24,7 +25,7 @@ class SubmissionNotice {
|
|||
|| document.getElementById("movie_player") || document.querySelector("#player-container .video-js");
|
||||
if (referenceNode == null) {
|
||||
//for embeds
|
||||
let player = document.getElementById("player");
|
||||
const player = document.getElementById("player");
|
||||
referenceNode = player.firstChild as HTMLElement;
|
||||
let index = 1;
|
||||
|
||||
|
@ -51,11 +52,11 @@ class SubmissionNotice {
|
|||
);
|
||||
}
|
||||
|
||||
update() {
|
||||
update(): void {
|
||||
this.noticeRef.current.forceUpdate();
|
||||
}
|
||||
|
||||
close() {
|
||||
close(): void {
|
||||
ReactDOM.unmountComponentAtNode(this.noticeElement);
|
||||
|
||||
this.noticeElement.remove();
|
||||
|
|
19
src/types.ts
19
src/types.ts
|
@ -63,6 +63,21 @@ interface PreviewBarOption {
|
|||
opacity: string
|
||||
}
|
||||
|
||||
|
||||
interface Registration {
|
||||
message: string,
|
||||
id: string,
|
||||
allFrames: boolean,
|
||||
js: browser.extensionTypes.ExtensionFileOrCode[],
|
||||
css: browser.extensionTypes.ExtensionFileOrCode[],
|
||||
matches: string[]
|
||||
}
|
||||
|
||||
interface BackgroundScriptContainer {
|
||||
registerFirefoxContentScript: (opts: Registration) => void,
|
||||
unregisterFirefoxContentScript: (id: string) => void
|
||||
}
|
||||
|
||||
type VideoID = string;
|
||||
|
||||
export {
|
||||
|
@ -74,5 +89,7 @@ export {
|
|||
SponsorTime,
|
||||
VideoID,
|
||||
SponsorHideType,
|
||||
PreviewBarOption
|
||||
PreviewBarOption,
|
||||
Registration,
|
||||
BackgroundScriptContainer
|
||||
};
|
80
src/utils.ts
80
src/utils.ts
|
@ -1,12 +1,12 @@
|
|||
import Config from "./config";
|
||||
import { CategorySelection, SponsorTime, FetchResponse } from "./types";
|
||||
import { CategorySelection, SponsorTime, FetchResponse, BackgroundScriptContainer, Registration } from "./types";
|
||||
|
||||
import * as CompileConfig from "../config.json";
|
||||
|
||||
class Utils {
|
||||
|
||||
// Contains functions needed from the background script
|
||||
backgroundScriptContainer: any = null;
|
||||
backgroundScriptContainer: BackgroundScriptContainer | null = null;
|
||||
|
||||
// Used to add content scripts and CSS required
|
||||
js = [
|
||||
|
@ -19,24 +19,24 @@ class Utils {
|
|||
"popup.css"
|
||||
];
|
||||
|
||||
constructor(backgroundScriptContainer?: any) {
|
||||
constructor(backgroundScriptContainer?: BackgroundScriptContainer) {
|
||||
this.backgroundScriptContainer = backgroundScriptContainer;
|
||||
}
|
||||
|
||||
// Function that can be used to wait for a condition before returning
|
||||
async wait(condition, timeout = 5000, check = 100) {
|
||||
async wait(condition: () => HTMLElement | boolean, timeout = 5000, check = 100): Promise<HTMLElement | boolean> {
|
||||
return await new Promise((resolve, reject) => {
|
||||
setTimeout(() => reject("TIMEOUT"), timeout);
|
||||
|
||||
let intervalCheck = () => {
|
||||
let result = condition();
|
||||
const intervalCheck = () => {
|
||||
const result = condition();
|
||||
if (result !== false) {
|
||||
resolve(result);
|
||||
clearInterval(interval);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
let interval = setInterval(intervalCheck, check);
|
||||
const interval = setInterval(intervalCheck, check);
|
||||
|
||||
//run the check once first, this speeds it up a lot
|
||||
intervalCheck();
|
||||
|
@ -51,12 +51,12 @@ class Utils {
|
|||
*
|
||||
* @param {CallableFunction} callback
|
||||
*/
|
||||
setupExtraSitePermissions(callback) {
|
||||
setupExtraSitePermissions(callback: (granted: boolean) => void): void {
|
||||
// Request permission
|
||||
let permissions = ["declarativeContent"];
|
||||
if (this.isFirefox()) permissions = [];
|
||||
|
||||
let self = this;
|
||||
const self = this;
|
||||
|
||||
chrome.permissions.request({
|
||||
origins: this.getInvidiousInstancesRegex(),
|
||||
|
@ -79,20 +79,20 @@ class Utils {
|
|||
*
|
||||
* For now, it is just SB.config.invidiousInstances.
|
||||
*/
|
||||
setupExtraSiteContentScripts() {
|
||||
let self = this;
|
||||
setupExtraSiteContentScripts(): void {
|
||||
const self = this;
|
||||
|
||||
if (this.isFirefox()) {
|
||||
let firefoxJS = [];
|
||||
const firefoxJS = [];
|
||||
for (const file of this.js) {
|
||||
firefoxJS.push({file});
|
||||
}
|
||||
let firefoxCSS = [];
|
||||
const firefoxCSS = [];
|
||||
for (const file of this.css) {
|
||||
firefoxCSS.push({file});
|
||||
}
|
||||
|
||||
let registration = {
|
||||
const registration: Registration = {
|
||||
message: "registerContentScript",
|
||||
id: "invidious",
|
||||
allFrames: true,
|
||||
|
@ -108,7 +108,7 @@ class Utils {
|
|||
}
|
||||
} else {
|
||||
chrome.declarativeContent.onPageChanged.removeRules(["invidious"], function() {
|
||||
let conditions = [];
|
||||
const conditions = [];
|
||||
for (const regex of self.getInvidiousInstancesRegex()) {
|
||||
conditions.push(new chrome.declarativeContent.PageStateMatcher({
|
||||
pageUrl: { urlMatches: regex }
|
||||
|
@ -116,7 +116,7 @@ class Utils {
|
|||
}
|
||||
|
||||
// Add page rule
|
||||
let rule = {
|
||||
const rule = {
|
||||
id: "invidious",
|
||||
conditions,
|
||||
// This API is experimental and not visible by the TypeScript compiler
|
||||
|
@ -135,9 +135,9 @@ class Utils {
|
|||
/**
|
||||
* Removes the permission and content script registration.
|
||||
*/
|
||||
removeExtraSiteRegistration() {
|
||||
removeExtraSiteRegistration(): void {
|
||||
if (this.isFirefox()) {
|
||||
let id = "invidious";
|
||||
const id = "invidious";
|
||||
|
||||
if (this.backgroundScriptContainer) {
|
||||
this.backgroundScriptContainer.unregisterFirefoxContentScript(id);
|
||||
|
@ -163,7 +163,7 @@ class Utils {
|
|||
* @param sponsorTimes
|
||||
*/
|
||||
getSegmentsFromSponsorTimes(sponsorTimes: SponsorTime[]): number[][] {
|
||||
let segments: number[][] = [];
|
||||
const segments: number[][] = [];
|
||||
for (const sponsorTime of sponsorTimes) {
|
||||
segments.push(sponsorTime.segment);
|
||||
}
|
||||
|
@ -193,19 +193,19 @@ class Utils {
|
|||
}
|
||||
}
|
||||
|
||||
localizeHtmlPage() {
|
||||
localizeHtmlPage(): void {
|
||||
//Localize by replacing __MSG_***__ meta tags
|
||||
var objects = document.getElementsByClassName("sponsorBlockPageBody")[0].children;
|
||||
for (var j = 0; j < objects.length; j++) {
|
||||
var obj = objects[j];
|
||||
const objects = document.getElementsByClassName("sponsorBlockPageBody")[0].children;
|
||||
for (let j = 0; j < objects.length; j++) {
|
||||
const obj = objects[j];
|
||||
|
||||
let localizedMessage = this.getLocalizedMessage(obj.innerHTML.toString());
|
||||
const localizedMessage = this.getLocalizedMessage(obj.innerHTML.toString());
|
||||
if (localizedMessage) obj.innerHTML = localizedMessage;
|
||||
}
|
||||
}
|
||||
|
||||
getLocalizedMessage(text) {
|
||||
var valNewH = text.replace(/__MSG_(\w+)__/g, function(match, v1) {
|
||||
getLocalizedMessage(text: string): string | false {
|
||||
const valNewH = text.replace(/__MSG_(\w+)__/g, function(match, v1) {
|
||||
return v1 ? chrome.i18n.getMessage(v1) : "";
|
||||
});
|
||||
|
||||
|
@ -219,8 +219,8 @@ class Utils {
|
|||
/**
|
||||
* @returns {String[]} Invidious Instances in regex form
|
||||
*/
|
||||
getInvidiousInstancesRegex() {
|
||||
var invidiousInstancesRegex = [];
|
||||
getInvidiousInstancesRegex(): string[] {
|
||||
const invidiousInstancesRegex: string[] = [];
|
||||
for (const url of Config.config.invidiousInstances) {
|
||||
invidiousInstancesRegex.push("https://*." + url + "/*");
|
||||
invidiousInstancesRegex.push("http://*." + url + "/*");
|
||||
|
@ -229,11 +229,11 @@ class Utils {
|
|||
return invidiousInstancesRegex;
|
||||
}
|
||||
|
||||
generateUserID(length = 36) {
|
||||
let charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
generateUserID(length = 36): string {
|
||||
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
let result = "";
|
||||
if (window.crypto && window.crypto.getRandomValues) {
|
||||
let values = new Uint32Array(length);
|
||||
const values = new Uint32Array(length);
|
||||
window.crypto.getRandomValues(values);
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += charset[values[i] % charset.length];
|
||||
|
@ -253,7 +253,7 @@ class Utils {
|
|||
* @param {int} statusCode
|
||||
* @returns {string} errorMessage
|
||||
*/
|
||||
getErrorMessage(statusCode) {
|
||||
getErrorMessage(statusCode: number): string {
|
||||
let errorMessage = "";
|
||||
|
||||
if([400, 429, 409, 502, 0].includes(statusCode)) {
|
||||
|
@ -298,7 +298,7 @@ class Utils {
|
|||
* @param callback
|
||||
*/
|
||||
async asyncRequestToServer(type: string, address: string, data = {}): Promise<FetchResponse> {
|
||||
let serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
|
||||
const serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
|
||||
|
||||
return await (this.asyncRequestToCustomServer(type, serverAddress + address, data));
|
||||
}
|
||||
|
@ -310,8 +310,8 @@ class Utils {
|
|||
* @param address The address to add to the SponsorBlock server address
|
||||
* @param callback
|
||||
*/
|
||||
sendRequestToServer(type: string, address: string, callback?: (response: FetchResponse) => void) {
|
||||
let serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
|
||||
sendRequestToServer(type: string, address: string, callback?: (response: FetchResponse) => void): void {
|
||||
const serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
|
||||
|
||||
// Ask the background script to do the work
|
||||
chrome.runtime.sendMessage({
|
||||
|
@ -324,15 +324,15 @@ class Utils {
|
|||
}
|
||||
|
||||
getFormattedTime(seconds: number, precise?: boolean): string {
|
||||
let hours = Math.floor(seconds / 60 / 60);
|
||||
let minutes = Math.floor(seconds / 60) % 60;
|
||||
const hours = Math.floor(seconds / 60 / 60);
|
||||
const minutes = Math.floor(seconds / 60) % 60;
|
||||
let minutesDisplay = String(minutes);
|
||||
let secondsNum = seconds % 60;
|
||||
if (!precise) {
|
||||
secondsNum = Math.floor(secondsNum);
|
||||
}
|
||||
|
||||
let secondsDisplay: string = String(precise ? secondsNum.toFixed(3) : secondsNum);
|
||||
let secondsDisplay = String(precise ? secondsNum.toFixed(3) : secondsNum);
|
||||
|
||||
if (secondsNum < 10) {
|
||||
//add a zero
|
||||
|
@ -343,7 +343,7 @@ class Utils {
|
|||
minutesDisplay = "0" + minutesDisplay;
|
||||
}
|
||||
|
||||
let formatted = (hours ? hours + ":" : "") + minutesDisplay + ":" + secondsDisplay;
|
||||
const formatted = (hours ? hours + ":" : "") + minutesDisplay + ":" + secondsDisplay;
|
||||
|
||||
return formatted;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue