Merge pull request #564 from FoseFx/fosefx-eslint

ESLint
This commit is contained in:
Ajay Ramachandran
2020-12-14 13:11:56 -05:00
committed by GitHub
22 changed files with 1819 additions and 642 deletions

33
.eslintrc.js Normal file
View 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

File diff suppressed because it is too large Load Diff

View File

@@ -19,7 +19,11 @@
"@types/firefox-webext-browser": "70.0.1", "@types/firefox-webext-browser": "70.0.1",
"@types/jest": "^24.0.23", "@types/jest": "^24.0.23",
"@types/jquery": "^3.3.31", "@types/jquery": "^3.3.31",
"@typescript-eslint/eslint-plugin": "^4.9.1",
"@typescript-eslint/parser": "^4.9.1",
"copy-webpack-plugin": "^6.0.3", "copy-webpack-plugin": "^6.0.3",
"eslint": "^7.15.0",
"eslint-plugin-react": "^7.21.5",
"jest": "^26.4.0", "jest": "^26.4.0",
"rimraf": "^3.0.0", "rimraf": "^3.0.0",
"ts-jest": "^26.2.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": "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\"", "dev:firefox": "npm run build:dev:firefox && concurrently \"npm run web-run:firefox\" \"npm run build:watch:firefox\"",
"clean": "rimraf dist", "clean": "rimraf dist",
"test": "npx jest" "test": "npx jest",
"lint": "eslint src",
"lint:fix": "eslint src --fix"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

@@ -1855,7 +1855,7 @@ THE SOFTWARE.
async-each async-each
1.0.3 <https://github.com/paulmillr/async-each> 1.0.3 <https://github.com/paulmillr/async-each>
license: MIT 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. 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 &lt;nathan@tootallnate.net&gt;
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 bluebird
@@ -4918,6 +4946,32 @@ OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
USE OR PERFORMANCE OF THIS SOFTWARE. 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 fill-range
@@ -5153,6 +5207,34 @@ the licensed code:
DEALINGS IN THE SOFTWARE. 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 gensync
@@ -6709,6 +6791,25 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. 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 nanomatch
@@ -8350,7 +8451,7 @@ SOFTWARE.
run-queue run-queue
1.0.3 <https://github.com/iarna/run-queue> 1.0.3 <https://github.com/iarna/run-queue>
license: ISC 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 spdx-license-ids
3.0.5 <https://github.com/shinnn/spdx-license-ids> 3.0.5 <https://github.com/shinnn/spdx-license-ids>
license: CC0-1.0 license: CC0-1.0
authors: Shinnosuke Watanabe <https://github.com/shinnn> authors: Shinnosuke Watanabe (https://github.com/shinnn)
****************************** ******************************

View File

@@ -1,17 +1,18 @@
import * as CompileConfig from "../config.json"; import * as CompileConfig from "../config.json";
import Config from "./config"; import Config from "./config";
import { Registration } from "./types";
// Make the config public for debugging purposes // Make the config public for debugging purposes
(<any> window).SB = Config; (<any> window).SB = Config;
import Utils from "./utils"; import Utils from "./utils";
var utils = new Utils({ const utils = new Utils({
registerFirefoxContentScript, registerFirefoxContentScript,
unregisterFirefoxContentScript unregisterFirefoxContentScript
}); });
// Used only on Firefox, which does not support non persistent background pages. // Used only on Firefox, which does not support non persistent background pages.
var contentScriptRegistrations = {}; const contentScriptRegistrations = {};
// Register content script if needed // Register content script if needed
if (utils.isFirefox()) { if (utils.isFirefox()) {
@@ -58,6 +59,7 @@ chrome.runtime.onMessage.addListener(function (request, sender, callback) {
iconUrl: "./icons/LogoSponsorBlocker256px.png" iconUrl: "./icons/LogoSponsorBlocker256px.png"
}); });
} }
break;
case "registerContentScript": case "registerContentScript":
registerFirefoxContentScript(request); registerFirefoxContentScript(request);
return false; return false;
@@ -93,8 +95,8 @@ chrome.runtime.onInstalled.addListener(function (object) {
* *
* @param {JSON} options * @param {JSON} options
*/ */
function registerFirefoxContentScript(options) { function registerFirefoxContentScript(options: Registration) {
let oldRegistration = contentScriptRegistrations[options.id]; const oldRegistration = contentScriptRegistrations[options.id];
if (oldRegistration) oldRegistration.unregister(); if (oldRegistration) oldRegistration.unregister();
browser.contentScripts.register({ browser.contentScripts.register({
@@ -124,10 +126,10 @@ async function submitVote(type: number, UUID: string, category: string) {
Config.config.userID = userID; Config.config.userID = userID;
} }
let typeSection = (type !== undefined) ? "&type=" + type : "&category=" + category; const typeSection = (type !== undefined) ? "&type=" + type : "&category=" + category;
//publish this vote //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) { if (response.ok) {
return { return {
@@ -149,7 +151,7 @@ async function submitVote(type: number, UUID: string, category: string) {
} }
async function asyncRequestToServer(type: string, address: string, data = {}) { 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)); 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 GET, convert JSON to parameters
if (type.toLowerCase() === "get") { if (type.toLowerCase() === "get") {
for (const key in data) { for (const key in data) {
let seperator = url.includes("?") ? "&" : "?"; const seperator = url.includes("?") ? "&" : "?";
let value = (typeof(data[key]) === "string") ? data[key]: JSON.stringify(data[key]); const value = (typeof(data[key]) === "string") ? data[key]: JSON.stringify(data[key]);
url += seperator + key + "=" + value; url += seperator + key + "=" + value;
} }

View File

@@ -23,7 +23,7 @@ class CategoryChooserComponent extends React.Component<CategoryChooserProps, Cat
} }
} }
render() { render(): React.ReactElement {
return ( return (
<table id="categoryChooserTable" <table id="categoryChooserTable"
className="categoryChooserTable"> className="categoryChooserTable">
@@ -55,7 +55,7 @@ class CategoryChooserComponent extends React.Component<CategoryChooserProps, Cat
} }
getCategorySkipOptions(): JSX.Element[] { getCategorySkipOptions(): JSX.Element[] {
let elements: JSX.Element[] = []; const elements: JSX.Element[] = [];
for (const category of CompileConfig.categoryList) { for (const category of CompileConfig.categoryList) {
elements.push( elements.push(

View File

@@ -29,7 +29,7 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
} }
} }
render() { render(): React.ReactElement {
let defaultOption = "disable"; let defaultOption = "disable";
// Set the default opton properly // Set the default opton properly
for (const categorySelection of Config.config.categorySelections) { for (const categorySelection of Config.config.categorySelections) {
@@ -145,9 +145,9 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
} }
getCategorySkipOptions(): JSX.Element[] { 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) { for (const optionName of optionNames) {
elements.push( elements.push(
@@ -160,7 +160,7 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
return elements; return elements;
} }
setColorState(event: React.FormEvent<HTMLInputElement>, preview: boolean) { setColorState(event: React.FormEvent<HTMLInputElement>, preview: boolean): void {
if (preview) { if (preview) {
this.setState({ this.setState({
previewColor: event.currentTarget.value previewColor: event.currentTarget.value

View File

@@ -35,7 +35,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
constructor(props: NoticeProps) { constructor(props: NoticeProps) {
super(props); super(props);
let maxCountdownTime = () => { const maxCountdownTime = () => {
if (this.props.maxCountdownTime) return this.props.maxCountdownTime(); if (this.props.maxCountdownTime) return this.props.maxCountdownTime();
else return 4; else return 4;
}; };
@@ -60,12 +60,12 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
} }
} }
componentDidMount() { componentDidMount(): void {
this.startCountdown(); this.startCountdown();
} }
render() { render(): React.ReactElement {
let noticeStyle: React.CSSProperties = { const noticeStyle: React.CSSProperties = {
zIndex: this.props.zIndex || (50 + this.amountOfPreviousNotices) 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; if (this.state.countdownManuallyPaused) return;
this.pauseCountdown(); this.pauseCountdown();
} }
timerMouseLeave() { timerMouseLeave(): void {
if (this.state.countdownManuallyPaused) return; if (this.state.countdownManuallyPaused) return;
this.startCountdown(); this.startCountdown();
} }
toggleManualPause() { toggleManualPause(): void {
this.setState({ this.setState({
countdownManuallyPaused: !this.state.countdownManuallyPaused 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 //called every second to lower the countdown before hiding the notice
countdown() { countdown(): void {
if (!this.props.timed) return; if (!this.props.timed) return;
let countdownTime = this.state.countdownTime - 1; const countdownTime = this.state.countdownTime - 1;
if (countdownTime <= 0) { if (countdownTime <= 0) {
//remove this from setInterval //remove this from setInterval
@@ -166,7 +166,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
if (countdownTime == 3) { if (countdownTime == 3) {
//start fade out animation //start fade out animation
let notice = document.getElementById("sponsorSkipNotice" + this.idSuffix); const notice = document.getElementById("sponsorSkipNotice" + this.idSuffix);
notice.style.removeProperty("animation"); notice.style.removeProperty("animation");
notice.classList.add("sponsorSkipNoticeFadeOut"); notice.classList.add("sponsorSkipNoticeFadeOut");
} }
@@ -176,7 +176,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
}) })
} }
pauseCountdown() { pauseCountdown(): void {
if (!this.props.timed) return; if (!this.props.timed) return;
//remove setInterval //remove setInterval
@@ -190,12 +190,12 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
}); });
//remove the fade out class if it exists //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.classList.remove("sponsorSkipNoticeFadeOut");
notice.style.animation = "none"; notice.style.animation = "none";
} }
startCountdown() { startCountdown(): void {
if (!this.props.timed) return; if (!this.props.timed) return;
//if it has already started, don't start it again //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); this.countdownInterval = setInterval(this.countdown.bind(this), 1000);
} }
resetCountdown() { resetCountdown(): void {
if (!this.props.timed) return; if (!this.props.timed) return;
this.setState({ this.setState({
@@ -221,36 +221,36 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
/** /**
* @param silent If true, the close listener will not be called * @param silent If true, the close listener will not be called
*/ */
close(silent?: boolean) { close(silent?: boolean): void {
//remove setInterval //remove setInterval
if (this.countdownInterval !== null) clearInterval(this.countdownInterval); if (this.countdownInterval !== null) clearInterval(this.countdownInterval);
if (!silent) this.props.closeListener(); if (!silent) this.props.closeListener();
} }
changeNoticeTitle(title) { changeNoticeTitle(title: string): void {
this.setState({ this.setState({
noticeTitle: title noticeTitle: title
}); });
} }
addNoticeInfoMessage(message: string, message2: string = "") { addNoticeInfoMessage(message: string, message2 = ""): void {
//TODO: Replace //TODO: Replace
let previousInfoMessage = document.getElementById("sponsorTimesInfoMessage" + this.idSuffix); const previousInfoMessage = document.getElementById("sponsorTimesInfoMessage" + this.idSuffix);
if (previousInfoMessage != null) { if (previousInfoMessage != null) {
//remove it //remove it
document.getElementById("sponsorSkipNotice" + this.idSuffix).removeChild(previousInfoMessage); 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) { if (previousInfoMessage2 != null) {
//remove it //remove it
document.getElementById("sponsorSkipNotice" + this.idSuffix).removeChild(previousInfoMessage2); document.getElementById("sponsorSkipNotice" + this.idSuffix).removeChild(previousInfoMessage2);
} }
//add info //add info
let thanksForVotingText = document.createElement("p"); const thanksForVotingText = document.createElement("p");
thanksForVotingText.id = "sponsorTimesInfoMessage" + this.idSuffix; thanksForVotingText.id = "sponsorTimesInfoMessage" + this.idSuffix;
thanksForVotingText.className = "sponsorTimesInfoMessage"; thanksForVotingText.className = "sponsorTimesInfoMessage";
thanksForVotingText.innerText = message; 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)); document.querySelector("#sponsorSkipNotice" + this.idSuffix + " > tbody").insertBefore(thanksForVotingText, document.getElementById("sponsorSkipNoticeSpacer" + this.idSuffix));
if (message2 !== undefined) { if (message2 !== undefined) {
let thanksForVotingText2 = document.createElement("p"); const thanksForVotingText2 = document.createElement("p");
thanksForVotingText2.id = "sponsorTimesInfoMessage" + this.idSuffix + "2"; thanksForVotingText2.id = "sponsorTimesInfoMessage" + this.idSuffix + "2";
thanksForVotingText2.className = "sponsorTimesInfoMessage"; thanksForVotingText2.className = "sponsorTimesInfoMessage";
thanksForVotingText2.innerText = message2; thanksForVotingText2.innerText = message2;

View File

@@ -16,8 +16,8 @@ class NoticeTextSelectionComponent extends React.Component<NoticeTextSelectionPr
super(props); super(props);
} }
render() { render(): React.ReactElement {
let style: React.CSSProperties = {}; const style: React.CSSProperties = {};
if (this.props.onClick) { if (this.props.onClick) {
style.cursor = "pointer"; style.cursor = "pointer";
style.textDecoration = "underline" style.textDecoration = "underline"

View File

@@ -4,7 +4,7 @@ import Config from "../config"
import { ContentContainer, SponsorHideType, SponsorTime } from "../types"; import { ContentContainer, SponsorHideType, SponsorTime } from "../types";
import Utils from "../utils"; import Utils from "../utils";
var utils = new Utils(); const utils = new Utils();
import NoticeComponent from "./NoticeComponent"; import NoticeComponent from "./NoticeComponent";
import NoticeTextSelectionComponent from "./NoticeTextSectionComponent"; import NoticeTextSelectionComponent from "./NoticeTextSectionComponent";
@@ -31,7 +31,7 @@ export interface SkipNoticeState {
noticeTitle: string; noticeTitle: string;
messages: string[]; messages: string[];
messageOnClick: (event: React.MouseEvent) => any; messageOnClick: (event: React.MouseEvent) => unknown;
countdownTime: number; countdownTime: number;
maxCountdownTime: () => number; maxCountdownTime: () => number;
@@ -56,7 +56,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
amountOfPreviousNotices: number; amountOfPreviousNotices: number;
audio: HTMLAudioElement; audio: HTMLAudioElement;
idSuffix: any; idSuffix: string;
noticeRef: React.MutableRefObject<NoticeComponent>; noticeRef: React.MutableRefObject<NoticeComponent>;
categoryOptionRef: React.RefObject<HTMLSelectElement>; categoryOptionRef: React.RefObject<HTMLSelectElement>;
@@ -74,7 +74,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
this.contentContainer = props.contentContainer; this.contentContainer = props.contentContainer;
this.audio = null; 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); : "category_" + this.segments[0].category + "_short") || chrome.i18n.getMessage("category_" + this.segments[0].category);
let noticeTitle = categoryName + " " + chrome.i18n.getMessage("skipped"); let noticeTitle = categoryName + " " + chrome.i18n.getMessage("skipped");
if (!this.autoSkip) { if (!this.autoSkip) {
@@ -98,7 +98,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
if (this.amountOfPreviousNotices > 0) { if (this.amountOfPreviousNotices > 0) {
//another notice exists //another notice exists
let previousNotice = document.getElementsByClassName("sponsorSkipNotice")[0]; const previousNotice = document.getElementsByClassName("sponsorSkipNotice")[0];
previousNotice.classList.add("secondSkipNotice") previousNotice.classList.add("secondSkipNotice")
} }
@@ -129,15 +129,15 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
} }
} }
componentDidMount() { componentDidMount(): void {
if (Config.config.audioNotificationOnSkip && this.audio) { if (Config.config.audioNotificationOnSkip && this.audio) {
this.audio.volume = this.contentContainer().v.volume * 0.1; this.audio.volume = this.contentContainer().v.volume * 0.1;
if (this.autoSkip) this.audio.play(); if (this.autoSkip) this.audio.play();
} }
} }
render() { render(): React.ReactElement {
let noticeStyle: React.CSSProperties = { const noticeStyle: React.CSSProperties = {
zIndex: 50 + this.amountOfPreviousNotices zIndex: 50 + this.amountOfPreviousNotices
} }
if (this.contentContainer().onMobileYouTube) { if (this.contentContainer().onMobileYouTube) {
@@ -286,7 +286,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
} }
getSubmissionChooser(): JSX.Element[] { getSubmissionChooser(): JSX.Element[] {
let elements: JSX.Element[] = []; const elements: JSX.Element[] = [];
for (let i = 0; i < this.segments.length; i++) { for (let i = 0; i < this.segments.length; i++) {
elements.push( elements.push(
@@ -301,7 +301,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
return elements; return elements;
} }
prepAction(action: SkipNoticeAction) { prepAction(action: SkipNoticeAction): void {
if (this.segments.length === 1) { if (this.segments.length === 1) {
this.performAction(0, action); this.performAction(0, action);
} else { } 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++) { for (let i = 0; i < this.state.messages.length; i++) {
elements.push( elements.push(
@@ -341,7 +341,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
* *
* @param index * @param index
*/ */
performAction(index: number, action?: SkipNoticeAction) { performAction(index: number, action?: SkipNoticeAction): void {
switch (action ?? this.state.actionState) { switch (action ?? this.state.actionState) {
case SkipNoticeAction.None: case SkipNoticeAction.None:
break; break;
@@ -364,7 +364,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
}); });
} }
adjustDownvotingState(value: boolean) { adjustDownvotingState(value: boolean): void {
if (!value) this.clearConfigListener(); if (!value) this.clearConfigListener();
this.setState({ this.setState({
@@ -373,14 +373,14 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
}); });
} }
clearConfigListener() { clearConfigListener(): void {
if (this.configListener) { if (this.configListener) {
Config.configListeners.splice(Config.configListeners.indexOf(this.configListener), 1); Config.configListeners.splice(Config.configListeners.indexOf(this.configListener), 1);
this.configListener = null; this.configListener = null;
} }
} }
openCategoryChooser() { openCategoryChooser(): void {
// Add as a config listener // Add as a config listener
this.configListener = () => this.forceUpdate(); this.configListener = () => this.forceUpdate();
Config.configListeners.push(this.configListener); Config.configListeners.push(this.configListener);
@@ -396,8 +396,8 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
}); });
} }
getCategoryOptions() { getCategoryOptions(): React.ReactElement[] {
let elements = []; const elements = [];
for (const category of Config.config.categorySelections) { for (const category of Config.config.categorySelections) {
elements.push( elements.push(
@@ -421,7 +421,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
return elements; return elements;
} }
categorySelectionChange(event: React.ChangeEvent<HTMLSelectElement>) { categorySelectionChange(event: React.ChangeEvent<HTMLSelectElement>): void {
// See if show more categories was pressed // See if show more categories was pressed
if (event.target.value === "moreCategories") { if (event.target.value === "moreCategories") {
// Open options page // 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.contentContainer().unskipSponsorTime(this.segments[index]);
this.unskippedMode(index, chrome.i18n.getMessage("reskip")); this.unskippedMode(index, chrome.i18n.getMessage("reskip"));
} }
/** Sets up notice to be not skipped yet */ /** 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 //setup new callback and reset countdown
this.setState(this.getUnskippedModeInfo(index, buttonText), () => { this.setState(this.getUnskippedModeInfo(index, buttonText), () => {
this.noticeRef.current.resetCountdown(); this.noticeRef.current.resetCountdown();
@@ -448,10 +448,10 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
} }
getUnskippedModeInfo(index: number, buttonText: string) { getUnskippedModeInfo(index: number, buttonText: string) {
let self = this; const self = this;
let maxCountdownTime = function() { const maxCountdownTime = function() {
let sponsorTime = self.segments[index]; const sponsorTime = self.segments[index];
let duration = Math.round((sponsorTime.segment[1] - self.contentContainer().v.currentTime) * (1 / self.contentContainer().v.playbackRate)); const duration = Math.round((sponsorTime.segment[1] - self.contentContainer().v.currentTime) * (1 / self.contentContainer().v.playbackRate));
return Math.max(duration, 4); 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]); this.contentContainer().reskipSponsorTime(this.segments[index]);
//reset countdown //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")); this.addVoteButtonInfo(chrome.i18n.getMessage("voted"));
if (type === 0) { 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({ this.setState({
messages, messages,
messageOnClick: (event) => onClick(event) messageOnClick: (event) => onClick(event)
}); });
} }
setNoticeInfoMessage(...messages: string[]) { setNoticeInfoMessage(...messages: string[]): void {
this.setState({ this.setState({
messages messages
}); });
} }
addVoteButtonInfo(message) { addVoteButtonInfo(message): void {
this.setState({ this.setState({
thanksForVotingText: message thanksForVotingText: message
}); });
} }
resetVoteButtonInfo() { resetVoteButtonInfo(): void {
this.setState({ this.setState({
thanksForVotingText: null thanksForVotingText: null
}); });
} }
closeListener() { closeListener(): void {
this.clearConfigListener(); this.clearConfigListener();
this.props.closeListener(); this.props.closeListener();

View File

@@ -6,7 +6,7 @@ import * as CompileConfig from "../../config.json";
import Utils from "../utils"; import Utils from "../utils";
import { ContentContainer, SponsorTime } from "../types"; import { ContentContainer, SponsorTime } from "../types";
import SubmissionNoticeComponent from "./SubmissionNoticeComponent"; import SubmissionNoticeComponent from "./SubmissionNoticeComponent";
var utils = new Utils(); const utils = new Utils();
export interface SponsorTimeEditProps { export interface SponsorTimeEditProps {
index: number, index: number,
@@ -44,7 +44,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
}; };
} }
componentDidMount() { componentDidMount(): void {
// Prevent inputs from triggering key events // Prevent inputs from triggering key events
document.getElementById("sponsorTimesContainer" + this.idSuffix).addEventListener('keydown', function (event) { document.getElementById("sponsorTimesContainer" + this.idSuffix).addEventListener('keydown', function (event) {
event.stopPropagation(); event.stopPropagation();
@@ -57,14 +57,14 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
} }
} }
componentWillUnmount() { componentWillUnmount(): void {
if (this.configUpdateListener) { if (this.configUpdateListener) {
Config.configListeners.splice(Config.configListeners.indexOf(this.configUpdate.bind(this)), 1); Config.configListeners.splice(Config.configListeners.indexOf(this.configUpdate.bind(this)), 1);
} }
} }
render() { render(): React.ReactElement {
let style: React.CSSProperties = { const style: React.CSSProperties = {
textAlign: "center" textAlign: "center"
}; };
@@ -74,7 +74,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
// This method is required to get !important // This method is required to get !important
// https://stackoverflow.com/a/45669262/1985387 // https://stackoverflow.com/a/45669262/1985387
let oldYouTubeDarkStyles = (node) => { const oldYouTubeDarkStyles = (node) => {
if (node) { if (node) {
node.style.setProperty("color", "black", "important"); node.style.setProperty("color", "black", "important");
node.style.setProperty("text-shadow", "none", "important"); node.style.setProperty("text-shadow", "none", "important");
@@ -83,8 +83,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
// Create time display // Create time display
let timeDisplay: JSX.Element; let timeDisplay: JSX.Element;
let sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index]; const sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
let segment = sponsorTime.segment; const segment = sponsorTime.segment;
if (this.state.editing) { if (this.state.editing) {
timeDisplay = ( timeDisplay = (
<div id={"sponsorTimesContainer" + this.idSuffix} <div id={"sponsorTimesContainer" + this.idSuffix}
@@ -102,7 +102,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
type="text" type="text"
value={this.state.sponsorTimeEdits[0]} value={this.state.sponsorTimeEdits[0]}
onChange={(e) => { onChange={(e) => {
let sponsorTimeEdits = this.state.sponsorTimeEdits; const sponsorTimeEdits = this.state.sponsorTimeEdits;
sponsorTimeEdits[0] = e.target.value; sponsorTimeEdits[0] = e.target.value;
this.setState({sponsorTimeEdits}); this.setState({sponsorTimeEdits});
@@ -121,7 +121,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
type="text" type="text"
value={this.state.sponsorTimeEdits[1]} value={this.state.sponsorTimeEdits[1]}
onChange={(e) => { onChange={(e) => {
let sponsorTimeEdits = this.state.sponsorTimeEdits; const sponsorTimeEdits = this.state.sponsorTimeEdits;
sponsorTimeEdits[1] = e.target.value; sponsorTimeEdits[1] = e.target.value;
this.setState({sponsorTimeEdits}); this.setState({sponsorTimeEdits});
@@ -215,8 +215,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
); );
} }
getCategoryOptions() { getCategoryOptions(): React.ReactElement[] {
let elements = [( const elements = [(
<option value={"chooseACategory"} <option value={"chooseACategory"}
key={"chooseACategory"}> key={"chooseACategory"}>
{chrome.i18n.getMessage("chooseACategory")} {chrome.i18n.getMessage("chooseACategory")}
@@ -245,7 +245,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
return elements; return elements;
} }
categorySelectionChange(event: React.ChangeEvent<HTMLSelectElement>) { categorySelectionChange(event: React.ChangeEvent<HTMLSelectElement>): void {
// See if show more categories was pressed // See if show more categories was pressed
if (event.target.value === "moreCategories") { if (event.target.value === "moreCategories") {
// Open options page // Open options page
@@ -259,16 +259,16 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
this.saveEditTimes(); this.saveEditTimes();
} }
setTimeToNow(index: number) { setTimeToNow(index: number): void {
this.setTimeTo(index, this.props.contentContainer().getRealCurrentTime()); this.setTimeTo(index, this.props.contentContainer().getRealCurrentTime());
} }
setTimeToEnd() { setTimeToEnd(): void {
this.setTimeTo(1, this.props.contentContainer().v.duration); this.setTimeTo(1, this.props.contentContainer().v.duration);
} }
setTimeTo(index: number, time: number) { setTimeTo(index: number, time: number): void {
let sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index]; const sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
sponsorTime.segment[index] = sponsorTime.segment[index] =
time; time;
@@ -287,7 +287,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
this.saveEditTimes(); this.saveEditTimes();
} else { } else {
let sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index]; const sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
this.setState({ this.setState({
editing: true, editing: true,
@@ -302,8 +302,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
utils.getFormattedTime(sponsorTime.segment[1], true)]; utils.getFormattedTime(sponsorTime.segment[1], true)];
} }
saveEditTimes() { saveEditTimes(): void {
let sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting; const sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting;
if (this.state.editing) { if (this.state.editing) {
const startTime = utils.getFormattedTimeToSeconds(this.state.sponsorTimeEdits[0]); const startTime = utils.getFormattedTimeToSeconds(this.state.sponsorTimeEdits[0]);
@@ -323,26 +323,26 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
} }
previewTime(): void { previewTime(): void {
let sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting; const sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
let index = this.props.index; const index = this.props.index;
let skipTime = sponsorTimes[index].segment[0]; const skipTime = sponsorTimes[index].segment[0];
this.props.contentContainer().previewTime(skipTime - 2); this.props.contentContainer().previewTime(skipTime - 2);
} }
inspectTime(): void { inspectTime(): void {
let sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting; const sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
let index = this.props.index; 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); this.props.contentContainer().previewTime(skipTime + 0.000001, false);
} }
deleteTime(): void { deleteTime(): void {
let sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting; const sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
let index = this.props.index; const index = this.props.index;
//if it is not a complete sponsor time //if it is not a complete sponsor time
if (sponsorTimes[index].segment.length < 2) { if (sponsorTimes[index].segment.length < 2) {
@@ -369,7 +369,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
} }
} }
configUpdate() { configUpdate(): void {
this.forceUpdate(); this.forceUpdate();
} }
} }

View File

@@ -10,7 +10,7 @@ export interface SubmissionNoticeProps {
// Contains functions and variables from the content script needed by the skip notice // Contains functions and variables from the content script needed by the skip notice
contentContainer: ContentContainer; contentContainer: ContentContainer;
callback: () => any; callback: () => unknown;
closeListener: () => void 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 // Contains functions and variables from the content script needed by the skip notice
contentContainer: ContentContainer; contentContainer: ContentContainer;
callback: () => any; callback: () => unknown;
noticeRef: React.MutableRefObject<NoticeComponent>; noticeRef: React.MutableRefObject<NoticeComponent>;
timeEditRefs: React.RefObject<SponsorTimeEditComponent>[]; timeEditRefs: React.RefObject<SponsorTimeEditComponent>[];
@@ -39,7 +39,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
this.contentContainer = props.contentContainer; this.contentContainer = props.contentContainer;
this.callback = props.callback; this.callback = props.callback;
let noticeTitle = chrome.i18n.getMessage("confirmNoticeTitle"); const noticeTitle = chrome.i18n.getMessage("confirmNoticeTitle");
// Setup state // Setup state
this.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 // Catch and rerender when the video size changes
//TODO: Use ResizeObserver when it is supported in TypeScript //TODO: Use ResizeObserver when it is supported in TypeScript
this.videoObserver = new MutationObserver(() => { this.videoObserver = new MutationObserver(() => {
@@ -61,13 +61,13 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
}); });
} }
componentWillUnmount() { componentWillUnmount(): void {
if (this.videoObserver) { if (this.videoObserver) {
this.videoObserver.disconnect(); this.videoObserver.disconnect();
} }
} }
render() { render(): React.ReactElement {
return ( return (
<NoticeComponent noticeTitle={this.state.noticeTitle} <NoticeComponent noticeTitle={this.state.noticeTitle}
idSuffix={this.state.idSuffix} idSuffix={this.state.idSuffix}
@@ -114,13 +114,13 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
} }
getSponsorTimeMessages(): JSX.Element[] | JSX.Element { getSponsorTimeMessages(): JSX.Element[] | JSX.Element {
let elements: JSX.Element[] = []; const elements: JSX.Element[] = [];
this.timeEditRefs = []; this.timeEditRefs = [];
let sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting; const sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
for (let i = 0; i < sponsorTimes.length; i++) { for (let i = 0; i < sponsorTimes.length; i++) {
let timeRef = React.createRef<SponsorTimeEditComponent>(); const timeRef = React.createRef<SponsorTimeEditComponent>();
elements.push( elements.push(
<SponsorTimeEditComponent key={i} <SponsorTimeEditComponent key={i}
@@ -139,7 +139,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
} }
getMessageBoxes(): JSX.Element[] | JSX.Element { getMessageBoxes(): JSX.Element[] | JSX.Element {
let elements: JSX.Element[] = []; const elements: JSX.Element[] = [];
for (let i = 0; i < this.state.messages.length; i++) { for (let i = 0; i < this.state.messages.length; i++) {
elements.push( elements.push(
@@ -153,7 +153,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
return elements; return elements;
} }
cancel() { cancel(): void {
this.noticeRef.current.close(true); this.noticeRef.current.close(true);
this.contentContainer().resetSponsorSubmissionNotice(); this.contentContainer().resetSponsorSubmissionNotice();
@@ -161,13 +161,13 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
this.props.closeListener(); this.props.closeListener();
} }
submit() { submit(): void {
// save all items // save all items
for (const ref of this.timeEditRefs) { for (const ref of this.timeEditRefs) {
ref.current.saveEditTimes(); ref.current.saveEditTimes();
} }
let sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting; const sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting;
for (const sponsorTime of sponsorTimesSubmitting) { for (const sponsorTime of sponsorTimesSubmitting) {
if (sponsorTime.category === "chooseACategory") { if (sponsorTime.category === "chooseACategory") {
alert(chrome.i18n.getMessage("youMustSelectACategory")); alert(chrome.i18n.getMessage("youMustSelectACategory"));

View File

@@ -132,7 +132,7 @@ class SBMap<T, U> extends Map {
} }
} }
var Config: SBObject = { const Config: SBObject = {
/** /**
* Callback function when an option is updated * Callback function when an option is updated
*/ */
@@ -149,7 +149,7 @@ var Config: SBObject = {
skipCount: 0, skipCount: 0,
sponsorTimesContributed: 0, sponsorTimesContributed: 0,
submissionCountSinceCategories: 0, submissionCountSinceCategories: 0,
showTimeWithSkips: true, showTimeWithSkips: true,
unsubmittedWarning: true, unsubmittedWarning: true,
disableSkipping: false, disableSkipping: false,
trackViewCount: true, trackViewCount: true,
@@ -286,7 +286,7 @@ function configProxy(): any {
} }
}); });
var handler: ProxyHandler<any> = { const handler: ProxyHandler<any> = {
set(obj, prop, value) { set(obj, prop, value) {
Config.localConfig[prop] = value; Config.localConfig[prop] = value;
@@ -298,7 +298,7 @@ function configProxy(): any {
}, },
get(obj, prop): any { get(obj, prop): any {
let data = Config.localConfig[prop]; const data = Config.localConfig[prop];
return obj[prop] || data; return obj[prop] || data;
}, },
@@ -314,7 +314,7 @@ function configProxy(): any {
return new Proxy({handler}, handler); return new Proxy({handler}, handler);
} }
function fetchConfig() { function fetchConfig(): Promise<void> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
chrome.storage.sync.get(null, function(items) { chrome.storage.sync.get(null, function(items) {
Config.localConfig = <SBConfig> <unknown> items; // Data is ready Config.localConfig = <SBConfig> <unknown> items; // Data is ready
@@ -351,7 +351,7 @@ function migrateOldFormats(config: SBConfig) {
if (config.whitelistedChannels.length > 0 && if (config.whitelistedChannels.length > 0 &&
(config.whitelistedChannels[0] == null || config.whitelistedChannels[0].includes("/"))) { (config.whitelistedChannels[0] == null || config.whitelistedChannels[0].includes("/"))) {
const channelURLFixer = async() => { const channelURLFixer = async() => {
let newChannelList: string[] = []; const newChannelList: string[] = [];
for (const item of config.whitelistedChannels) { for (const item of config.whitelistedChannels) {
if (item != null) { if (item != null) {
if (item.includes("/channel/")) { if (item.includes("/channel/")) {
@@ -360,7 +360,7 @@ function migrateOldFormats(config: SBConfig) {
// Replace channel URL with channelID // 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) { if (response.ok) {
newChannelList.push((JSON.parse(response.responseText)).authorId); newChannelList.push((JSON.parse(response.responseText)).authorId);
@@ -408,9 +408,9 @@ function migrateOldFormats(config: SBConfig) {
// Otherwise junk data // Otherwise junk data
if (Array.isArray(jsonData)) { if (Array.isArray(jsonData)) {
let oldMap = new Map(jsonData); const oldMap = new Map(jsonData);
oldMap.forEach((sponsorTimes: number[][], key) => { oldMap.forEach((sponsorTimes: number[][], key) => {
let segmentTimes: SponsorTime[] = []; const segmentTimes: SponsorTime[] = [];
for (const segment of sponsorTimes) { for (const segment of sponsorTimes) {
segmentTimes.push({ segmentTimes.push({
segment: segment, segment: segment,
@@ -442,7 +442,7 @@ async function setupConfig() {
// Reset config // Reset config
function resetConfig() { function resetConfig() {
Config.config = Config.defaults; Config.config = Config.defaults;
}; }
function convertJSON(): void { function convertJSON(): void {
Object.keys(Config.localConfig).forEach(key => { Object.keys(Config.localConfig).forEach(key => {
@@ -453,17 +453,17 @@ function convertJSON(): void {
// Add defaults // Add defaults
function addDefaults() { function addDefaults() {
for (const key in Config.defaults) { 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]; Config.localConfig[key] = Config.defaults[key];
} else if (key === "barTypes") { } else if (key === "barTypes") {
for (const key2 in Config.defaults[key]) { 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]; Config.localConfig[key][key2] = Config.defaults[key][key2];
} }
} }
} }
} }
}; }
// Sync config // Sync config
setupConfig(); setupConfig();

View File

@@ -4,7 +4,7 @@ import { SponsorTime, CategorySkipOption, CategorySelection, VideoID, SponsorHid
import { ContentContainer } from "./types"; import { ContentContainer } from "./types";
import Utils from "./utils"; import Utils from "./utils";
var utils = new Utils(); const utils = new Utils();
import runThePopup from "./popup"; import runThePopup from "./popup";
@@ -17,80 +17,80 @@ import SubmissionNotice from "./render/SubmissionNotice";
utils.wait(() => Config.config !== null, 5000, 10).then(addCSS); utils.wait(() => Config.config !== null, 5000, 10).then(addCSS);
//was sponsor data found when doing SponsorsLookup //was sponsor data found when doing SponsorsLookup
var sponsorDataFound = false; let sponsorDataFound = false;
var previousVideoID: VideoID = null; let previousVideoID: VideoID = null;
//the actual sponsorTimes if loaded and UUIDs associated with them //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 //what video id are these sponsors for
var sponsorVideoID: VideoID = null; let sponsorVideoID: VideoID = null;
// JSON video info // JSON video info
var videoInfo: any = null; let videoInfo: any = null;
//the channel this video is about //the channel this video is about
var channelID; let channelID;
// Skips are scheduled to ensure precision. // Skips are scheduled to ensure precision.
// Skips are rescheduled every seeking event. // Skips are rescheduled every seeking event.
// Skips are canceled every seeking event // Skips are canceled every seeking event
var currentSkipSchedule: NodeJS.Timeout = null; let currentSkipSchedule: NodeJS.Timeout = null;
var seekListenerSetUp = false let seekListenerSetUp = false
/** @type {Array[boolean]} Has the sponsor been skipped */ /** @type {Array[boolean]} Has the sponsor been skipped */
var sponsorSkipped: boolean[] = []; let sponsorSkipped: boolean[] = [];
//the video //the video
var video: HTMLVideoElement; let video: HTMLVideoElement;
var onInvidious; let onInvidious;
var onMobileYouTube; let onMobileYouTube;
//the video id of the last preview bar update //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 //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 // 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 // Used by the play and playing listeners to make sure two aren't
// called at the same time // called at the same time
var lastCheckTime = 0; let lastCheckTime = 0;
var lastCheckVideoTime = -1; let lastCheckVideoTime = -1;
//is this channel whitelised from getting sponsors skipped //is this channel whitelised from getting sponsors skipped
var channelWhitelisted = false; let channelWhitelisted = false;
// create preview bar // create preview bar
var previewBar: PreviewBar = null; let previewBar: PreviewBar = null;
//the player controls on the YouTube player //the player controls on the YouTube player
var controls = null; let controls = null;
// Direct Links after the config is loaded // Direct Links after the config is loaded
utils.wait(() => Config.config !== null, 1000, 1).then(() => videoIDChange(getYouTubeVideoID(document.URL))); utils.wait(() => Config.config !== null, 1000, 1).then(() => videoIDChange(getYouTubeVideoID(document.URL)));
//the amount of times the sponsor lookup has retried //the amount of times the sponsor lookup has retried
//this only happens if there is an error //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 //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 //the sponsor times being prepared to be submitted
var sponsorTimesSubmitting: SponsorTime[] = []; let sponsorTimesSubmitting: SponsorTime[] = [];
//becomes true when isInfoFound is called //becomes true when isInfoFound is called
//this is used to close the popup on YouTube when the other popup opens //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 // 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 // Contains all of the functions and variables needed by the skip notice
var skipNoticeContentContainer: ContentContainer = () => ({ const skipNoticeContentContainer: ContentContainer = () => ({
vote, vote,
dontShowNoticeAgain, dontShowNoticeAgain,
unskipSponsorTime, unskipSponsorTime,
@@ -218,13 +218,13 @@ if (!Config.configListeners.includes(contentConfigUpdateListener)) {
//check for hotkey pressed //check for hotkey pressed
document.onkeydown = function(e: KeyboardEvent){ 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 //is the video in focus, otherwise they could be typing a comment
if (document.activeElement === video) { if (document.activeElement === video) {
@@ -296,7 +296,7 @@ async function videoIDChange(id) {
} }
if (isUnlisted()) { if (isUnlisted()) {
let shouldContinue = confirm(chrome.i18n.getMessage("confirmPrivacy")); const shouldContinue = confirm(chrome.i18n.getMessage("confirmPrivacy"));
if(!shouldContinue) return; if(!shouldContinue) return;
} }
} }
@@ -323,7 +323,7 @@ async function videoIDChange(id) {
//warn them if they had unsubmitted times //warn them if they had unsubmitted times
if (previousVideoID != null) { if (previousVideoID != null) {
//get the sponsor times from storage //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") { if (sponsorTimes != undefined && sponsorTimes.length > 0 && new URL(document.URL).host !== "music.youtube.com") {
//warn them that they have unsubmitted sponsor times //warn them that they have unsubmitted sponsor times
chrome.runtime.sendMessage({ chrome.runtime.sendMessage({
@@ -347,7 +347,7 @@ async function videoIDChange(id) {
//make sure everything is properly added //make sure everything is properly added
updateVisibilityOfPlayerControlsButton().then(() => { updateVisibilityOfPlayerControlsButton().then(() => {
//see if the onvideo control image needs to be changed //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) { if (segments != null && segments.length > 0 && segments[segments.length - 1].segment.length >= 2) {
changeStartSponsorButton(true, true); changeStartSponsorButton(true, true);
} else if (segments != null && segments.length > 0 && segments[segments.length - 1].segment.length < 2) { } 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 { function handleMobileControlsMutations(): void {
let mobileYouTubeSelector = ".progress-bar-background"; const mobileYouTubeSelector = ".progress-bar-background";
updateVisibilityOfPlayerControlsButton().then((createdButtons) => { updateVisibilityOfPlayerControlsButton().then((createdButtons) => {
if (createdButtons) { if (createdButtons) {
@@ -470,14 +470,14 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
if (currentTime === undefined || currentTime === null) currentTime = video.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; if (skipInfo.index === -1) return;
let currentSkip = skipInfo.array[skipInfo.index]; const currentSkip = skipInfo.array[skipInfo.index];
let skipTime: number[] = [currentSkip.segment[0], skipInfo.array[skipInfo.endIndex].segment[1]]; const skipTime: number[] = [currentSkip.segment[0], skipInfo.array[skipInfo.endIndex].segment[1]];
let timeUntilSponsor = skipTime[0] - currentTime; const timeUntilSponsor = skipTime[0] - currentTime;
let videoID = sponsorVideoID; const videoID = sponsorVideoID;
// Find all indexes in between the start and end // Find all indexes in between the start and end
let skippingSegments = [skipInfo.array[skipInfo.index]]; let skippingSegments = [skipInfo.array[skipInfo.index]];
@@ -496,7 +496,7 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
if (utils.getCategorySelection(currentSkip.category)?.option === CategorySkipOption.ShowOverlay if (utils.getCategorySelection(currentSkip.category)?.option === CategorySkipOption.ShowOverlay
&& skipInfo.array !== sponsorTimesSubmitting) return; && skipInfo.array !== sponsorTimesSubmitting) return;
let skippingFunction = () => { const skippingFunction = () => {
let forcedSkipTime: number = null; let forcedSkipTime: number = null;
let forcedIncludeIntersectingSegments = false; let forcedIncludeIntersectingSegments = false;
let forcedIncludeNonIntersectingSegments = true; 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 * This makes sure the videoID is still correct and if the sponsorTime is included
*/ */
function incorrectVideoCheck(videoID?: string, sponsorTime?: SponsorTime): boolean { 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))) { if (currentVideoID !== (videoID || sponsorVideoID) || (sponsorTime && (!sponsorTimes || !sponsorTimes.includes(sponsorTime)) && !sponsorTimesSubmitting.includes(sponsorTime))) {
// Something has really gone wrong // Something has really gone wrong
console.error("[SponsorBlock] The videoID recorded when trying to skip is different than what it should be."); 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 //made true once a setTimeout has been created to try again after a server error
let recheckStarted = false; let recheckStarted = false;
// Create categories list // Create categories list
let categories: string[] = []; const categories: string[] = [];
for (const categorySelection of Config.config.categorySelections) { for (const categorySelection of Config.config.categorySelections) {
categories.push(categorySelection.name); categories.push(categorySelection.name);
} }
@@ -649,7 +649,7 @@ async function sponsorsLookup(id: string) {
} }
} }
let recievedSegments: SponsorTime[] = result; const recievedSegments: SponsorTime[] = result;
if (!recievedSegments.length) { if (!recievedSegments.length) {
console.error("[SponsorBlock] Server returned malformed response: " + JSON.stringify(recievedSegments)); console.error("[SponsorBlock] Server returned malformed response: " + JSON.stringify(recievedSegments));
return; return;
@@ -713,7 +713,7 @@ function retryFetch(id: string): void {
//check if this video was uploaded recently //check if this video was uploaded recently
utils.wait(() => !!videoInfo).then(() => { utils.wait(() => !!videoInfo).then(() => {
let dateUploaded = videoInfo?.microformat?.playerMicroformatRenderer?.uploadDate; const dateUploaded = videoInfo?.microformat?.playerMicroformatRenderer?.uploadDate;
//if less than 3 days old //if less than 3 days old
if (Date.now() - new Date(dateUploaded).getTime() < 259200000) { if (Date.now() - new Date(dateUploaded).getTime() < 259200000) {
@@ -733,7 +733,7 @@ function retryFetch(id: string): void {
function startSkipScheduleCheckingForStartSponsors() { function startSkipScheduleCheckingForStartSponsors() {
if (!switchingVideos) { if (!switchingVideos) {
// See if there are any starting sponsors // See if there are any starting sponsors
let startingSponsor: number = -1; let startingSponsor = -1;
for (const time of sponsorTimes) { for (const time of sponsorTimes) {
if (time.segment[0] <= video.currentTime && time.segment[0] > startingSponsor && time.segment[1] > video.currentTime) { if (time.segment[0] <= video.currentTime && time.segment[0] > startingSponsor && time.segment[1] > video.currentTime) {
startingSponsor = time.segment[0]; startingSponsor = time.segment[0];
@@ -763,7 +763,7 @@ function startSkipScheduleCheckingForStartSponsors() {
function getVideoInfo() { function getVideoInfo() {
sendRequestToCustomServer('GET', "https://www.youtube.com/get_video_info?video_id=" + sponsorVideoID, function(xmlhttp, error) { sendRequestToCustomServer('GET', "https://www.youtube.com/get_video_info?video_id=" + sponsorVideoID, function(xmlhttp, error) {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { 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) { if (!decodedData) {
console.error("[SB] Failed at getting video info from YouTube."); console.error("[SB] Failed at getting video info from YouTube.");
return; return;
@@ -803,7 +803,7 @@ function getYouTubeVideoID(url: string) {
//Get ID from searchParam //Get ID from searchParam
if (urlObject.searchParams.has("v") && ["/watch", "/watch/"].includes(urlObject.pathname) || urlObject.pathname.startsWith("/tv/watch")) { 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; return id.length == 11 ? id : false;
} else if (urlObject.pathname.startsWith("/embed/")) { } else if (urlObject.pathname.startsWith("/embed/")) {
try { try {
@@ -836,10 +836,10 @@ function updatePreviewBar() {
let localSponsorTimes = sponsorTimes; let localSponsorTimes = sponsorTimes;
if (localSponsorTimes == null) localSponsorTimes = []; if (localSponsorTimes == null) localSponsorTimes = [];
let allSponsorTimes = localSponsorTimes.concat(sponsorTimesSubmitting); const allSponsorTimes = localSponsorTimes.concat(sponsorTimesSubmitting);
//create an array of the sponsor types //create an array of the sponsor types
let types = []; const types = [];
for (let i = 0; i < localSponsorTimes.length; i++) { for (let i = 0; i < localSponsorTimes.length; i++) {
if (localSponsorTimes[i].hidden === SponsorHideType.Visible) { if (localSponsorTimes[i].hidden === SponsorHideType.Visible) {
types.push(localSponsorTimes[i].category); types.push(localSponsorTimes[i].category);
@@ -872,7 +872,7 @@ function whitelistCheck() {
} }
//see if this is a whitelisted channel //see if this is a whitelisted channel
let whitelistedChannels = Config.config.whitelistedChannels; const whitelistedChannels = Config.config.whitelistedChannels;
if (whitelistedChannels != undefined && whitelistedChannels.includes(channelID)) { if (whitelistedChannels != undefined && whitelistedChannels.includes(channelID)) {
channelWhitelisted = true; channelWhitelisted = true;
@@ -888,17 +888,17 @@ function whitelistCheck() {
function getNextSkipIndex(currentTime: number, includeIntersectingSegments: boolean, includeNonIntersectingSegments: boolean): function getNextSkipIndex(currentTime: number, includeIntersectingSegments: boolean, includeNonIntersectingSegments: boolean):
{array: SponsorTime[], index: number, endIndex: number, openNotice: boolean} { {array: SponsorTime[], index: number, endIndex: number, openNotice: boolean} {
let sponsorStartTimes = getStartTimes(sponsorTimes, includeIntersectingSegments, includeNonIntersectingSegments); const sponsorStartTimes = getStartTimes(sponsorTimes, includeIntersectingSegments, includeNonIntersectingSegments);
let sponsorStartTimesAfterCurrentTime = getStartTimes(sponsorTimes, includeIntersectingSegments, includeNonIntersectingSegments, currentTime, true, true); const sponsorStartTimesAfterCurrentTime = getStartTimes(sponsorTimes, includeIntersectingSegments, includeNonIntersectingSegments, currentTime, true, true);
let minSponsorTimeIndex = sponsorStartTimes.indexOf(Math.min(...sponsorStartTimesAfterCurrentTime)); const minSponsorTimeIndex = sponsorStartTimes.indexOf(Math.min(...sponsorStartTimesAfterCurrentTime));
let endTimeIndex = getLatestEndTimeIndex(sponsorTimes, minSponsorTimeIndex); const endTimeIndex = getLatestEndTimeIndex(sponsorTimes, minSponsorTimeIndex);
let previewSponsorStartTimes = getStartTimes(sponsorTimesSubmitting, includeIntersectingSegments, includeNonIntersectingSegments); const previewSponsorStartTimes = getStartTimes(sponsorTimesSubmitting, includeIntersectingSegments, includeNonIntersectingSegments);
let previewSponsorStartTimesAfterCurrentTime = getStartTimes(sponsorTimesSubmitting, includeIntersectingSegments, includeNonIntersectingSegments, currentTime, false, false); const previewSponsorStartTimesAfterCurrentTime = getStartTimes(sponsorTimesSubmitting, includeIntersectingSegments, includeNonIntersectingSegments, currentTime, false, false);
let minPreviewSponsorTimeIndex = previewSponsorStartTimes.indexOf(Math.min(...previewSponsorStartTimesAfterCurrentTime)); const minPreviewSponsorTimeIndex = previewSponsorStartTimes.indexOf(Math.min(...previewSponsorStartTimesAfterCurrentTime));
let previewEndTimeIndex = getLatestEndTimeIndex(sponsorTimesSubmitting, minPreviewSponsorTimeIndex); const previewEndTimeIndex = getLatestEndTimeIndex(sponsorTimesSubmitting, minPreviewSponsorTimeIndex);
if ((minPreviewSponsorTimeIndex === -1 && minSponsorTimeIndex !== -1) || if ((minPreviewSponsorTimeIndex === -1 && minSponsorTimeIndex !== -1) ||
sponsorStartTimes[minSponsorTimeIndex] < previewSponsorStartTimes[minPreviewSponsorTimeIndex]) { sponsorStartTimes[minSponsorTimeIndex] < previewSponsorStartTimes[minPreviewSponsorTimeIndex]) {
@@ -931,7 +931,7 @@ function getNextSkipIndex(currentTime: number, includeIntersectingSegments: bool
* @param index Index of the given sponsor * @param index Index of the given sponsor
* @param hideHiddenSponsors * @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 // Only combine segments for AutoSkip
if (index == -1 || if (index == -1 ||
utils.getCategorySelection(sponsorTimes[index].category)?.option !== CategorySkipOption.AutoSkip) return index; utils.getCategorySelection(sponsorTimes[index].category)?.option !== CategorySkipOption.AutoSkip) return index;
@@ -940,8 +940,8 @@ function getLatestEndTimeIndex(sponsorTimes: SponsorTime[], index: number, hideH
let latestEndTimeIndex = index; let latestEndTimeIndex = index;
for (let i = 0; i < sponsorTimes?.length; i++) { for (let i = 0; i < sponsorTimes?.length; i++) {
let currentSegment = sponsorTimes[i].segment; const currentSegment = sponsorTimes[i].segment;
let latestEndTime = sponsorTimes[latestEndTimeIndex].segment[1]; const latestEndTime = sponsorTimes[latestEndTimeIndex].segment[1];
if (currentSegment[0] <= latestEndTime && currentSegment[1] > latestEndTime if (currentSegment[0] <= latestEndTime && currentSegment[1] > latestEndTime
&& (!hideHiddenSponsors || sponsorTimes[i].hidden === SponsorHideType.Visible) && (!hideHiddenSponsors || sponsorTimes[i].hidden === SponsorHideType.Visible)
@@ -970,10 +970,10 @@ function getLatestEndTimeIndex(sponsorTimes: SponsorTime[], index: number, hideH
* the current time, but end after * the current time, but end after
*/ */
function getStartTimes(sponsorTimes: SponsorTime[], includeIntersectingSegments: boolean, includeNonIntersectingSegments: boolean, 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 []; if (sponsorTimes === null) return [];
let startTimes: number[] = []; const startTimes: number[] = [];
for (let i = 0; i < sponsorTimes?.length; i++) { for (let i = 0; i < sponsorTimes?.length; i++) {
if ((minimum === undefined 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 //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) { function skipToTime(v: HTMLVideoElement, skipTime: number[], skippingSegments: SponsorTime[], openNotice: boolean) {
// There will only be one submission if it is manual skip // 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]) { if ((autoSkip || sponsorTimesSubmitting.includes(skippingSegments[0])) && v.currentTime !== skipTime[1]) {
// Fix for looped videos not working when skipping to the end #426 // 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; let isPreviewSegment = false;
for (const segment of skippingSegments) { for (const segment of skippingSegments) {
let index = sponsorTimes.indexOf(segment); const index = sponsorTimes.indexOf(segment);
if (index !== -1 && !sponsorSkipped[index]) { if (index !== -1 && !sponsorSkipped[index]) {
utils.asyncRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + segment.UUID); 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; if (document.getElementById(baseID + "Button") != null) return false;
// Button HTML // Button HTML
let newButton = document.createElement("button"); const newButton = document.createElement("button");
newButton.draggable = isDraggable; newButton.draggable = isDraggable;
newButton.id = baseID + "Button"; newButton.id = baseID + "Button";
newButton.classList.add("playerButton"); newButton.classList.add("playerButton");
@@ -1077,7 +1077,7 @@ function createButton(baseID, title, callback, imageName, isDraggable=false): bo
}); });
// Image HTML // Image HTML
let newButtonImage = document.createElement("img"); const newButtonImage = document.createElement("img");
newButton.draggable = isDraggable; newButton.draggable = isDraggable;
newButtonImage.id = baseID + "Image"; newButtonImage.id = baseID + "Image";
newButtonImage.className = "playerButtonImage"; newButtonImage.className = "playerButtonImage";
@@ -1092,8 +1092,8 @@ function createButton(baseID, title, callback, imageName, isDraggable=false): bo
return true; return true;
} }
function getControls(): HTMLElement | boolean { function getControls(): HTMLElement | false {
let controlsSelectors = [ const controlsSelectors = [
// YouTube // YouTube
".ytp-right-controls", ".ytp-right-controls",
// Mobile YouTube // Mobile YouTube
@@ -1103,7 +1103,7 @@ function getControls(): HTMLElement | boolean {
] ]
for (const controlsSelector of controlsSelectors) { for (const controlsSelector of controlsSelectors) {
let controls = document.querySelectorAll(controlsSelector); const controls = document.querySelectorAll(controlsSelector);
if (controls && controls.length > 0) { if (controls && controls.length > 0) {
return <HTMLElement> controls[controls.length - 1]; return <HTMLElement> controls[controls.length - 1];
@@ -1111,13 +1111,13 @@ function getControls(): HTMLElement | boolean {
} }
return false; return false;
}; }
//adds all the player controls buttons //adds all the player controls buttons
async function createButtons(): Promise<boolean> { async function createButtons(): Promise<boolean> {
if (onMobileYouTube) return; if (onMobileYouTube) return;
let result = await utils.wait(getControls).catch(); const result = await utils.wait(getControls).catch();
//set global controls variable //set global controls variable
controls = result; controls = result;
@@ -1138,7 +1138,7 @@ async function updateVisibilityOfPlayerControlsButton(): Promise<boolean> {
//not on a proper video yet //not on a proper video yet
if (!sponsorVideoID) return false; if (!sponsorVideoID) return false;
let createdButtons = await createButtons(); const createdButtons = await createButtons();
if (Config.config.hideVideoPlayerControls || onInvidious) { if (Config.config.hideVideoPlayerControls || onInvidious) {
document.getElementById("startSponsorButton").style.display = "none"; document.getElementById("startSponsorButton").style.display = "none";
@@ -1168,8 +1168,8 @@ async function updateVisibilityOfPlayerControlsButton(): Promise<boolean> {
*/ */
function getRealCurrentTime(): number { function getRealCurrentTime(): number {
// Used to check if replay button // Used to check if replay button
let playButtonSVGData = document.querySelector(".ytp-play-button")?.querySelector(".ytp-svg-fill")?.getAttribute("d"); const 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 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) { if (playButtonSVGData === replaceSVGData) {
// At the end of the video // At the end of the video
@@ -1204,8 +1204,8 @@ function startSponsorClicked() {
updateSponsorTimesSubmitting(false) updateSponsorTimesSubmitting(false)
} }
function updateSponsorTimesSubmitting(getFromConfig: boolean = true) { function updateSponsorTimesSubmitting(getFromConfig = true) {
let segmentTimes = Config.config.segmentTimes.get(sponsorVideoID); const segmentTimes = Config.config.segmentTimes.get(sponsorVideoID);
//see if this data should be saved in the sponsorTimesSubmitting variable //see if this data should be saved in the sponsorTimesSubmitting variable
if (getFromConfig && segmentTimes != undefined) { if (getFromConfig && segmentTimes != undefined) {
@@ -1234,7 +1234,7 @@ async function changeStartSponsorButton(showStartSponsor, uploadButtonVisible) {
if(!sponsorVideoID) return false; if(!sponsorVideoID) return false;
//if it isn't visible, there is no data //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; document.getElementById("deleteButton").style.display = shouldHide;
if (showStartSponsor) { if (showStartSponsor) {
@@ -1275,7 +1275,7 @@ function openInfoMenu() {
sendRequestToCustomServer('GET', chrome.extension.getURL("popup.html"), function(xmlhttp) { sendRequestToCustomServer('GET', chrome.extension.getURL("popup.html"), function(xmlhttp) {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
let popup = document.createElement("div"); const popup = document.createElement("div");
popup.id = "sponsorBlockPopupContainer"; popup.id = "sponsorBlockPopupContainer";
let htmlData = xmlhttp.responseText; let htmlData = xmlhttp.responseText;
@@ -1288,7 +1288,7 @@ function openInfoMenu() {
popup.innerHTML = htmlData; popup.innerHTML = htmlData;
//close button //close button
let closeButton = document.createElement("div"); const closeButton = document.createElement("div");
closeButton.innerText = chrome.i18n.getMessage("closePopup"); closeButton.innerText = chrome.i18n.getMessage("closePopup");
closeButton.classList.add("smallLink"); closeButton.classList.add("smallLink");
closeButton.setAttribute("align", "center"); closeButton.setAttribute("align", "center");
@@ -1299,7 +1299,7 @@ function openInfoMenu() {
//add the close button //add the close button
popup.prepend(closeButton); popup.prepend(closeButton);
let parentNodes = document.querySelectorAll("#secondary"); const parentNodes = document.querySelectorAll("#secondary");
let parentNode = null; let parentNode = null;
for (let i = 0; i < parentNodes.length; i++) { for (let i = 0; i < parentNodes.length; i++) {
if (parentNodes[i].firstElementChild !== null) { if (parentNodes[i].firstElementChild !== null) {
@@ -1313,10 +1313,10 @@ function openInfoMenu() {
//make the logo source not 404 //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 //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"); const logo = <HTMLImageElement> popup.querySelector("#sponsorBlockPopupLogo");
let settings = <HTMLImageElement> popup.querySelector("#sbPopupIconSettings"); const settings = <HTMLImageElement> popup.querySelector("#sbPopupIconSettings");
let edit = <HTMLImageElement> popup.querySelector("#sbPopupIconEdit"); const edit = <HTMLImageElement> popup.querySelector("#sbPopupIconEdit");
let check = <HTMLImageElement> popup.querySelector("#sbPopupIconCheck"); const check = <HTMLImageElement> popup.querySelector("#sbPopupIconCheck");
logo.src = chrome.extension.getURL("icons/LogoSponsorBlocker256px.png"); logo.src = chrome.extension.getURL("icons/LogoSponsorBlocker256px.png");
settings.src = chrome.extension.getURL("icons/settings.svg"); settings.src = chrome.extension.getURL("icons/settings.svg");
edit.src = chrome.extension.getURL("icons/pencil.svg"); edit.src = chrome.extension.getURL("icons/pencil.svg");
@@ -1332,7 +1332,7 @@ function openInfoMenu() {
} }
function closeInfoMenu() { function closeInfoMenu() {
let popup = document.getElementById("sponsorBlockPopupContainer"); const popup = document.getElementById("sponsorBlockPopupContainer");
if (popup != null) { if (popup != null) {
popup.remove(); popup.remove();
@@ -1347,12 +1347,12 @@ function clearSponsorTimes() {
//it can't update to this info yet //it can't update to this info yet
closeInfoMenu(); 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) { 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") + "\n" + chrome.i18n.getMessage("confirmMSG")
if(!confirm(confirmMessage)) return; if(!confirm(confirmMessage)) return;
@@ -1377,7 +1377,7 @@ function vote(type: number, UUID: string, category?: string, skipNotice?: SkipNo
skipNotice.setNoticeInfoMessage.bind(skipNotice)(); skipNotice.setNoticeInfoMessage.bind(skipNotice)();
} }
let sponsorIndex = utils.getSponsorIndexFromUUID(sponsorTimes, UUID); const sponsorIndex = utils.getSponsorIndexFromUUID(sponsorTimes, UUID);
// Don't vote for preview sponsors // Don't vote for preview sponsors
if (sponsorIndex == -1 || sponsorTimes[sponsorIndex].UUID === null) return; 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 //Closes all notices that tell the user that a sponsor was just skipped
function closeAllSkipNotices(){ function closeAllSkipNotices(){
let notices = document.getElementsByClassName("sponsorSkipNotice"); const notices = document.getElementsByClassName("sponsorSkipNotice");
for (let i = 0; i < notices.length; i++) { for (let i = 0; i < notices.length; i++) {
notices[i].remove(); notices[i].remove();
} }
@@ -1456,7 +1456,7 @@ function submitSponsorTimes() {
//it can't update to this info yet //it can't update to this info yet
closeInfoMenu(); closeInfoMenu();
let currentVideoID = sponsorVideoID; const currentVideoID = sponsorVideoID;
if (sponsorTimesSubmitting !== undefined && sponsorTimesSubmitting.length > 0) { if (sponsorTimesSubmitting !== undefined && sponsorTimesSubmitting.length > 0) {
submissionNotice = new SubmissionNotice(skipNoticeContentContainer, sendSubmitMessage); submissionNotice = new SubmissionNotice(skipNoticeContentContainer, sendSubmitMessage);
@@ -1466,7 +1466,7 @@ function submitSponsorTimes() {
//send the message to the background js //send the message to the background js
//called after all the checks have been made that it's okay to do so //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 //add loading animation
(<HTMLImageElement> document.getElementById("submitImage")).src = chrome.extension.getURL("icons/PlayerUploadIconSponsorBlocker256px.png"); (<HTMLImageElement> document.getElementById("submitImage")).src = chrome.extension.getURL("icons/PlayerUploadIconSponsorBlocker256px.png");
document.getElementById("submitButton").style.animation = "rotate 1s 0s infinite"; document.getElementById("submitButton").style.animation = "rotate 1s 0s infinite";
@@ -1485,7 +1485,7 @@ async function sendSubmitMessage(){
if (Config.config.minDuration > 0) { if (Config.config.minDuration > 0) {
for (let i = 0; i < sponsorTimesSubmitting.length; i++) { for (let i = 0; i < sponsorTimesSubmitting.length; i++) {
if (sponsorTimesSubmitting[i].segment[1] - sponsorTimesSubmitting[i].segment[0] < Config.config.minDuration) { 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); getSegmentsMessage(sponsorTimesSubmitting);
if(!confirm(confirmShort)) return; 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, videoID: sponsorVideoID,
userID: Config.config.userID, userID: Config.config.userID,
segments: sponsorTimesSubmitting segments: sponsorTimesSubmitting
@@ -1501,11 +1501,11 @@ async function sendSubmitMessage(){
if (response.status === 200) { if (response.status === 200) {
//hide loading message //hide loading message
let submitButton = document.getElementById("submitButton"); const submitButton = document.getElementById("submitButton");
submitButton.style.animation = "rotate 1s"; submitButton.style.animation = "rotate 1s";
//finish this animation //finish this animation
//when the animation is over, hide the button //when the animation is over, hide the button
let animationEndListener = function() { const animationEndListener = function() {
changeStartSponsorButton(true, false); changeStartSponsorButton(true, false);
submitButton.style.animation = "none"; submitButton.style.animation = "none";
@@ -1579,10 +1579,10 @@ function isUnlisted(): boolean {
function addCSS() { function addCSS() {
if (!utils.isFirefox() && Config.config.invidiousInstances.includes(new URL(document.URL).host)) { if (!utils.isFirefox() && Config.config.invidiousInstances.includes(new URL(document.URL).host)) {
window.addEventListener("DOMContentLoaded", () => { window.addEventListener("DOMContentLoaded", () => {
let head = document.getElementsByTagName("head")[0]; const head = document.getElementsByTagName("head")[0];
for (const file of utils.css) { for (const file of utils.css) {
let fileref = document.createElement("link"); const fileref = document.createElement("link");
fileref.rel = "stylesheet"; fileref.rel = "stylesheet";
fileref.type = "text/css"; fileref.type = "text/css";
@@ -1595,7 +1595,7 @@ function addCSS() {
} }
function sendRequestToCustomServer(type, fullAddress, callback) { function sendRequestToCustomServer(type, fullAddress, callback) {
let xmlhttp = new XMLHttpRequest(); const xmlhttp = new XMLHttpRequest();
xmlhttp.open(type, fullAddress, true); 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 * Update the isAdPlaying flag and hide preview bar/controls if ad is playing
*/ */
function updateAdFlag() { function updateAdFlag() {
let wasAdPlaying = isAdPlaying; const wasAdPlaying = isAdPlaying;
isAdPlaying = document.getElementsByClassName('ad-showing').length > 0; isAdPlaying = document.getElementsByClassName('ad-showing').length > 0;
if(wasAdPlaying != isAdPlaying) { if(wasAdPlaying != isAdPlaying) {
@@ -1641,10 +1641,10 @@ function showTimeWithoutSkips(allSponsorTimes): void {
} }
// YouTube player time display // 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; if (!display) return;
let formatedTime = utils.getFormattedTime(video.duration - skipDuration); const formatedTime = utils.getFormattedTime(video.duration - skipDuration);
const durationID = "sponsorBlockDurationAfterSkips"; const durationID = "sponsorBlockDurationAfterSkips";
let duration = document.getElementById(durationID); let duration = document.getElementById(durationID);

View File

@@ -7,7 +7,7 @@
import Config from "../config"; import Config from "../config";
import Utils from "../utils"; import Utils from "../utils";
let utils = new Utils(); const utils = new Utils();
class PreviewBar { class PreviewBar {
container: HTMLUListElement; container: HTMLUListElement;
@@ -16,9 +16,9 @@ class PreviewBar {
onInvidious: boolean; onInvidious: boolean;
timestamps: number[][]; timestamps: number[][];
types: string; types: string[];
constructor(parent, onMobileYouTube, onInvidious) { constructor(parent: any, onMobileYouTube: boolean, onInvidious: boolean) {
this.container = document.createElement('ul'); this.container = document.createElement('ul');
this.container.id = 'previewbar'; this.container.id = 'previewbar';
this.parent = parent; this.parent = parent;
@@ -31,15 +31,15 @@ class PreviewBar {
this.setupHoverText(); this.setupHoverText();
} }
setupHoverText() { setupHoverText(): void {
if (this.onMobileYouTube || this.onInvidious) return; 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 // Create label placeholder
let tooltipTextWrapper = document.querySelector(".ytp-tooltip-text-wrapper"); const tooltipTextWrapper = document.querySelector(".ytp-tooltip-text-wrapper");
let titleTooltip = document.querySelector(".ytp-tooltip-title"); const titleTooltip = document.querySelector(".ytp-tooltip-title");
let categoryTooltip = document.createElement("div"); const categoryTooltip = document.createElement("div");
categoryTooltip.className = "sbHidden ytp-tooltip-title"; categoryTooltip.className = "sbHidden ytp-tooltip-title";
categoryTooltip.id = "sponsor-block-category-tooltip" categoryTooltip.id = "sponsor-block-category-tooltip"
@@ -64,12 +64,12 @@ class PreviewBar {
return; return;
} }
let tooltips = document.querySelectorAll(".ytp-tooltip-text"); const tooltips = document.querySelectorAll(".ytp-tooltip-text");
for (const tooltip of tooltips) { 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]))) { if (splitData.length === 2 && !isNaN(parseInt(splitData[0])) && !isNaN(parseInt(splitData[1]))) {
// Add label // 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 // Find category at that location
let category = null; let category = null;
@@ -112,7 +112,7 @@ class PreviewBar {
}); });
} }
updatePosition(parent) { updatePosition(parent: any): void {
//below the seek bar //below the seek bar
// this.parent.insertAdjacentElement("afterEnd", this.container); // this.parent.insertAdjacentElement("afterEnd", this.container);
@@ -129,15 +129,15 @@ class PreviewBar {
this.parent.insertAdjacentElement("afterBegin", this.container); this.parent.insertAdjacentElement("afterBegin", this.container);
} }
updateColor(segment, color, opacity) { updateColor(segment: string, color: string, opacity: string): void {
let bars = <NodeListOf<HTMLElement>> document.querySelectorAll('[data-vs-segment-type=' + segment + ']'); const bars = <NodeListOf<HTMLElement>> document.querySelectorAll('[data-vs-segment-type=' + segment + ']');
for (let bar of bars) { for (const bar of bars) {
bar.style.backgroundColor = color; bar.style.backgroundColor = color;
bar.style.opacity = opacity; bar.style.opacity = opacity;
} }
} }
set(timestamps, types, duration) { set(timestamps: number[][], types: string[], duration: number): void {
while (this.container.firstChild) { while (this.container.firstChild) {
this.container.removeChild(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 = (timestamps[i][1] - timestamps[i][0]) / duration * 100;
width = Math.floor(width * 100) / 100; width = Math.floor(width * 100) / 100;
let bar = this.createBar(); const bar = this.createBar();
bar.setAttribute('data-vs-segment-type', types[i]); bar.setAttribute('data-vs-segment-type', types[i]);
bar.style.backgroundColor = Config.config.barTypes[types[i]].color; bar.style.backgroundColor = Config.config.barTypes[types[i]].color;
@@ -171,14 +171,14 @@ class PreviewBar {
} }
} }
createBar() { createBar(): HTMLLIElement {
let bar = document.createElement('li'); const bar = document.createElement('li');
bar.classList.add('previewbar'); bar.classList.add('previewbar');
bar.innerHTML = '&nbsp;'; bar.innerHTML = '&nbsp;';
return bar; return bar;
} }
remove() { remove(): void {
this.container.remove(); this.container.remove();
this.container = undefined; this.container = undefined;
} }

View File

@@ -6,7 +6,7 @@ import * as CompileConfig from "../config.json";
import Utils from "./utils"; import Utils from "./utils";
import CategoryChooser from "./render/CategoryChooser"; import CategoryChooser from "./render/CategoryChooser";
var utils = new Utils(); const utils = new Utils();
window.addEventListener('DOMContentLoaded', init); window.addEventListener('DOMContentLoaded', init);
@@ -27,19 +27,19 @@ async function init() {
await utils.wait(() => Config.config !== null); await utils.wait(() => Config.config !== null);
// Set all of the toggle options to the correct option // Set all of the toggle options to the correct option
let optionsContainer = document.getElementById("options"); const optionsContainer = document.getElementById("options");
let optionsElements = optionsContainer.querySelectorAll("*"); const optionsElements = optionsContainer.querySelectorAll("*");
for (let i = 0; i < optionsElements.length; i++) { for (let i = 0; i < optionsElements.length; i++) {
switch (optionsElements[i].getAttribute("option-type")) { switch (optionsElements[i].getAttribute("option-type")) {
case "toggle": case "toggle": {
let option = optionsElements[i].getAttribute("sync-option"); const option = optionsElements[i].getAttribute("sync-option");
let optionResult = Config.config[option]; const optionResult = Config.config[option];
let checkbox = optionsElements[i].querySelector("input"); const checkbox = optionsElements[i].querySelector("input");
let reverse = optionsElements[i].getAttribute("toggle-type") === "reverse"; 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) { if (optionResult != undefined) {
checkbox.checked = optionResult; checkbox.checked = optionResult;
@@ -76,7 +76,7 @@ async function init() {
// Enable the notice // Enable the notice
Config.config["dontShowNotice"] = false; 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; showNoticeSwitch.checked = true;
} }
@@ -84,19 +84,20 @@ async function init() {
} }
}); });
break; break;
case "text-change": }
let textChangeOption = optionsElements[i].getAttribute("sync-option"); case "text-change": {
let textChangeInput = <HTMLInputElement> optionsElements[i].querySelector(".option-text-box"); 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]; textChangeInput.value = Config.config[textChangeOption];
textChangeSetButton.addEventListener("click", async () => { textChangeSetButton.addEventListener("click", async () => {
// See if anything extra must be done // See if anything extra must be done
switch (textChangeOption) { switch (textChangeOption) {
case "serverAddress": case "serverAddress": {
let result = validateServerAddress(textChangeInput.value); const result = validateServerAddress(textChangeInput.value);
if (result !== null) { if (result !== null) {
textChangeInput.value = result; textChangeInput.value = result;
@@ -106,7 +107,7 @@ async function init() {
// Permission needed on Firefox // Permission needed on Firefox
if (utils.isFirefox()) { if (utils.isFirefox()) {
let permissionSuccess = await new Promise((resolve, reject) => { const permissionSuccess = await new Promise((resolve, reject) => {
chrome.permissions.request({ chrome.permissions.request({
origins: [textChangeInput.value + "/"], origins: [textChangeInput.value + "/"],
permissions: [] permissions: []
@@ -117,13 +118,14 @@ async function init() {
} }
break; break;
}
} }
Config.config[textChangeOption] = textChangeInput.value; Config.config[textChangeOption] = textChangeInput.value;
}); });
// Reset to the default if needed // 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", () => { textChangeResetButton.addEventListener("click", () => {
if (!confirm(chrome.i18n.getMessage("areYouSureReset"))) return; if (!confirm(chrome.i18n.getMessage("areYouSureReset"))) return;
@@ -133,11 +135,12 @@ async function init() {
}); });
break; 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])); 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 // See if anything extra must be done
switch (privateTextChangeOption) { switch (privateTextChangeOption) {
case "invidiousInstances": case "invidiousInstances":
@@ -145,8 +148,9 @@ async function init() {
} }
break; 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")) { switch(optionsElements[i].getAttribute("sync-option")) {
case "copyDebugInformation": case "copyDebugInformation":
@@ -155,19 +159,21 @@ async function init() {
} }
break; 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])); keybindButton.addEventListener("click", () => activateKeybindChange(<HTMLElement> optionsElements[i]));
break; break;
case "display": }
case "display":{
updateDisplayElement(<HTMLElement> optionsElements[i]) updateDisplayElement(<HTMLElement> optionsElements[i])
break; break;
case "number-change": }
let numberChangeOption = optionsElements[i].getAttribute("sync-option"); case "number-change": {
let configValue = Config.config[numberChangeOption]; const numberChangeOption = optionsElements[i].getAttribute("sync-option");
let numberInput = optionsElements[i].querySelector("input"); const configValue = Config.config[numberChangeOption];
const numberInput = optionsElements[i].querySelector("input");
if (isNaN(configValue) || configValue < 0) { if (isNaN(configValue) || configValue < 0) {
numberInput.value = Config.defaults[numberChangeOption]; numberInput.value = Config.defaults[numberChangeOption];
@@ -180,6 +186,7 @@ async function init() {
}); });
break; break;
}
case "react-CategoryChooserComponent": case "react-CategoryChooserComponent":
new CategoryChooser(optionsElements[i]); new CategoryChooser(optionsElements[i]);
break; break;
@@ -196,8 +203,8 @@ async function init() {
* @param {String} element * @param {String} element
*/ */
function optionsConfigUpdateListener(changes) { function optionsConfigUpdateListener(changes) {
let optionsContainer = document.getElementById("options"); const optionsContainer = document.getElementById("options");
let optionsElements = optionsContainer.querySelectorAll("*"); const optionsElements = optionsContainer.querySelectorAll("*");
for (let i = 0; i < optionsElements.length; i++) { for (let i = 0; i < optionsElements.length; i++) {
switch (optionsElements[i].getAttribute("option-type")) { switch (optionsElements[i].getAttribute("option-type")) {
@@ -213,8 +220,8 @@ function optionsConfigUpdateListener(changes) {
* @param element * @param element
*/ */
function updateDisplayElement(element: HTMLElement) { function updateDisplayElement(element: HTMLElement) {
let displayOption = element.getAttribute("sync-option") const displayOption = element.getAttribute("sync-option")
let displayText = Config.config[displayOption]; const displayText = Config.config[displayOption];
element.innerText = displayText; element.innerText = displayText;
// See if anything extra must be run // See if anything extra must be run
@@ -232,10 +239,10 @@ function updateDisplayElement(element: HTMLElement) {
* @param option * @param option
*/ */
function invidiousInstanceAddInit(element: HTMLElement, option: string) { function invidiousInstanceAddInit(element: HTMLElement, option: string) {
let textBox = <HTMLInputElement> element.querySelector(".option-text-box"); const textBox = <HTMLInputElement> element.querySelector(".option-text-box");
let button = element.querySelector(".trigger-button"); 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) { setButton.addEventListener("click", async function(e) {
if (textBox.value == "" || textBox.value.includes("/") || textBox.value.includes("http")) { if (textBox.value == "" || textBox.value.includes("/") || textBox.value.includes("http")) {
alert(chrome.i18n.getMessage("addInvidiousInstanceError")); alert(chrome.i18n.getMessage("addInvidiousInstanceError"));
@@ -248,7 +255,7 @@ function invidiousInstanceAddInit(element: HTMLElement, option: string) {
Config.config[option] = instanceList; Config.config[option] = instanceList;
let checkbox = <HTMLInputElement> document.querySelector("#support-invidious input"); const checkbox = <HTMLInputElement> document.querySelector("#support-invidious input");
checkbox.checked = true; checkbox.checked = true;
invidiousOnClick(checkbox, "supportInvidious"); 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) { resetButton.addEventListener("click", function(e) {
if (confirm(chrome.i18n.getMessage("resetInvidiousInstanceAlert"))) { if (confirm(chrome.i18n.getMessage("resetInvidiousInstanceAlert"))) {
// Set to a clone of the default // Set to a clone of the default
@@ -298,7 +305,7 @@ function invidiousInit(checkbox: HTMLInputElement, option: string) {
* @param checkbox * @param checkbox
* @param option * @param option
*/ */
async function invidiousOnClick(checkbox: HTMLInputElement, option: string) { async function invidiousOnClick(checkbox: HTMLInputElement, option: string): Promise<void> {
return new Promise((resolve) => { return new Promise((resolve) => {
if (checkbox.checked) { if (checkbox.checked) {
utils.setupExtraSitePermissions(function (granted) { utils.setupExtraSitePermissions(function (granted) {
@@ -323,20 +330,20 @@ async function invidiousOnClick(checkbox: HTMLInputElement, option: string) {
* @param element * @param element
*/ */
function activateKeybindChange(element: HTMLElement) { function activateKeybindChange(element: HTMLElement) {
let button = element.querySelector(".trigger-button"); const button = element.querySelector(".trigger-button");
if (button.classList.contains("disabled")) return; if (button.classList.contains("disabled")) return;
button.classList.add("disabled"); 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; status.innerText = chrome.i18n.getMessage("keybindDescription") + currentlySet;
if (Config.config[option] !== null) { 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]; statusKey.innerText = Config.config[option];
} }
@@ -352,19 +359,19 @@ function activateKeybindChange(element: HTMLElement) {
* @param e * @param e
*/ */
function keybindKeyPressed(element: HTMLElement, e: KeyboardEvent) { 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) { if (["Shift", "Control", "Meta", "Alt", "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Tab"].indexOf(key) !== -1) {
// Wait for more // Wait for more
document.addEventListener("keydown", (e) => keybindKeyPressed(element, e), {once: true}); document.addEventListener("keydown", (e) => keybindKeyPressed(element, e), {once: true});
} else { } else {
let button: HTMLElement = element.querySelector(".trigger-button"); const button: HTMLElement = element.querySelector(".trigger-button");
let option = element.getAttribute("sync-option"); const option = element.getAttribute("sync-option");
// Make sure keybind isn't used by the other listener // 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. // 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) { if (key === otherKeybind) {
closeKeybindOption(element, button); closeKeybindOption(element, button);
@@ -381,10 +388,10 @@ function keybindKeyPressed(element: HTMLElement, e: KeyboardEvent) {
Config.config[option] = key; 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"); 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; statusKey.innerText = key;
button.classList.remove("disabled"); button.classList.remove("disabled");
@@ -408,13 +415,13 @@ function closeKeybindOption(element: HTMLElement, button: HTMLElement) {
* @param element * @param element
*/ */
function activatePrivateTextChange(element: HTMLElement) { function activatePrivateTextChange(element: HTMLElement) {
let button = element.querySelector(".trigger-button"); const button = element.querySelector(".trigger-button");
if (button.classList.contains("disabled")) return; if (button.classList.contains("disabled")) return;
button.classList.add("disabled"); button.classList.add("disabled");
let textBox = <HTMLInputElement> element.querySelector(".option-text-box"); const textBox = <HTMLInputElement> element.querySelector(".option-text-box");
let option = element.getAttribute("sync-option"); const option = element.getAttribute("sync-option");
// See if anything extra must be done // See if anything extra must be done
switch (option) { switch (option) {
@@ -427,21 +434,22 @@ function activatePrivateTextChange(element: HTMLElement) {
// See if anything extra must be done // See if anything extra must be done
switch (option) { switch (option) {
case "*": case "*": {
let jsonData = JSON.parse(JSON.stringify(Config.localConfig)); const jsonData = JSON.parse(JSON.stringify(Config.localConfig));
// Fix segmentTimes data as it is destroyed from the JSON stringify // Fix segmentTimes data as it is destroyed from the JSON stringify
jsonData.segmentTimes = Config.encodeStoredItem(Config.localConfig.segmentTimes); jsonData.segmentTimes = Config.encodeStoredItem(Config.localConfig.segmentTimes);
result = JSON.stringify(jsonData); result = JSON.stringify(jsonData);
break; break;
}
} }
textBox.value = result; textBox.value = result;
let setButton = element.querySelector(".text-change-set"); const setButton = element.querySelector(".text-change-set");
setButton.addEventListener("click", async () => { setButton.addEventListener("click", async () => {
let confirmMessage = element.getAttribute("confirm-message"); const confirmMessage = element.getAttribute("confirm-message");
if (confirmMessage === null || confirm(chrome.i18n.getMessage(confirmMessage))) { if (confirmMessage === null || confirm(chrome.i18n.getMessage(confirmMessage))) {
@@ -449,14 +457,14 @@ function activatePrivateTextChange(element: HTMLElement) {
switch (option) { switch (option) {
case "*": case "*":
try { try {
let newConfig = JSON.parse(textBox.value); const newConfig = JSON.parse(textBox.value);
for (const key in newConfig) { for (const key in newConfig) {
Config.config[key] = newConfig[key]; Config.config[key] = newConfig[key];
} }
Config.convertJSON(); Config.convertJSON();
if (newConfig.supportInvidious) { 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; checkbox.checked = true;
await invidiousOnClick(checkbox, "supportInvidious"); await invidiousOnClick(checkbox, "supportInvidious");
@@ -503,7 +511,7 @@ function validateServerAddress(input: string): string {
function copyDebugOutputToClipboard() { function copyDebugOutputToClipboard() {
// Build output debug information object // Build output debug information object
let output = { const output = {
debug: { debug: {
userAgent: navigator.userAgent, userAgent: navigator.userAgent,
platform: navigator.platform, platform: navigator.platform,
@@ -528,7 +536,7 @@ function copyDebugOutputToClipboard() {
.then(() => { .then(() => {
alert(chrome.i18n.getMessage("copyDebugInformationComplete")); alert(chrome.i18n.getMessage("copyDebugInformationComplete"));
}) })
.catch(err => { .catch((err) => {
alert(chrome.i18n.getMessage("copyDebugInformationFailed")); alert(chrome.i18n.getMessage("copyDebugInformationFailed"));
});; });
} }

View File

@@ -2,7 +2,7 @@ import Config from "./config";
import Utils from "./utils"; import Utils from "./utils";
import { SponsorTime, SponsorHideType } from "./types"; import { SponsorTime, SponsorHideType } from "./types";
var utils = new Utils(); const utils = new Utils();
interface MessageListener { interface MessageListener {
(request: any, sender: any, callback: (response: any) => void): void; (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 //make this a function to allow this to run on the content page
async function runThePopup(messageListener?: MessageListener) { async function runThePopup(messageListener?: MessageListener): Promise<void> {
var messageHandler = new MessageHandler(messageListener); const messageHandler = new MessageHandler(messageListener);
utils.localizeHtmlPage(); utils.localizeHtmlPage();
await utils.wait(() => Config.config !== null); await utils.wait(() => Config.config !== null);
var PageElements: any = {}; const PageElements: any = {};
[ [
"sponsorblockPopup", "sponsorblockPopup",
@@ -126,7 +126,7 @@ async function runThePopup(messageListener?: MessageListener) {
let currentVideoID = null; let currentVideoID = null;
//show proper disable skipping button //show proper disable skipping button
let disableSkipping = Config.config.disableSkipping; const disableSkipping = Config.config.disableSkipping;
if (disableSkipping != undefined && disableSkipping) { if (disableSkipping != undefined && disableSkipping) {
PageElements.disableSkipping.style.display = "none"; PageElements.disableSkipping.style.display = "none";
PageElements.enableSkipping.style.display = "unset"; 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 //if the don't show notice again variable is true, an option to
// disable should be available // disable should be available
let dontShowNotice = Config.config.dontShowNotice; const dontShowNotice = Config.config.dontShowNotice;
if (dontShowNotice != undefined && dontShowNotice) { if (dontShowNotice != undefined && dontShowNotice) {
PageElements.showNoticeAgain.style.display = "unset"; PageElements.showNoticeAgain.style.display = "unset";
} }
@@ -152,13 +152,13 @@ async function runThePopup(messageListener?: MessageListener) {
PageElements.sponsorTimesContributionsContainer.classList.remove("hidden"); PageElements.sponsorTimesContributionsContainer.classList.remove("hidden");
//get the userID //get the userID
let userID = Config.config.userID; const userID = Config.config.userID;
if (userID != undefined) { if (userID != undefined) {
//there are probably some views on these submissions then //there are probably some views on these submissions then
//get the amount of views from the sponsors submitted //get the amount of views from the sponsors submitted
utils.sendRequestToServer("GET", "/api/getViewsForUser?userID=" + userID, function(response) { utils.sendRequestToServer("GET", "/api/getViewsForUser?userID=" + userID, function(response) {
if (response.status == 200) { if (response.status == 200) {
let viewCount = JSON.parse(response.responseText).viewCount; const viewCount = JSON.parse(response.responseText).viewCount;
if (viewCount != 0) { if (viewCount != 0) {
if (viewCount > 1) { if (viewCount > 1) {
PageElements.sponsorTimesViewsDisplayEndWord.innerText = chrome.i18n.getMessage("Segments"); PageElements.sponsorTimesViewsDisplayEndWord.innerText = chrome.i18n.getMessage("Segments");
@@ -175,7 +175,7 @@ async function runThePopup(messageListener?: MessageListener) {
//get this time in minutes //get this time in minutes
utils.sendRequestToServer("GET", "/api/getSavedTimeForUser?userID=" + userID, function(response) { utils.sendRequestToServer("GET", "/api/getSavedTimeForUser?userID=" + userID, function(response) {
if (response.status == 200) { if (response.status == 200) {
let minutesSaved = JSON.parse(response.responseText).timeSaved; const minutesSaved = JSON.parse(response.responseText).timeSaved;
if (minutesSaved != 0) { if (minutesSaved != 0) {
if (minutesSaved != 1) { if (minutesSaved != 1) {
PageElements.sponsorTimesOthersTimeSavedEndWord.innerText = chrome.i18n.getMessage("minsLower"); PageElements.sponsorTimesOthersTimeSavedEndWord.innerText = chrome.i18n.getMessage("minsLower");
@@ -222,15 +222,15 @@ async function runThePopup(messageListener?: MessageListener) {
}, onTabs); }, onTabs);
function onTabs(tabs) { function onTabs(tabs) {
messageHandler.sendMessage(tabs[0].id, {message: 'getVideoID'}, function(result) { messageHandler.sendMessage(tabs[0].id, {message: 'getVideoID'}, function(result) {
if (result != undefined && result.videoID) { if (result != undefined && result.videoID) {
currentVideoID = result.videoID; currentVideoID = result.videoID;
loadTabData(tabs); loadTabData(tabs);
} else if (result == undefined && chrome.runtime.lastError) { } 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(); displayNoVideo();
} }
}); });
} }
function loadTabData(tabs) { function loadTabData(tabs) {
@@ -241,7 +241,7 @@ async function runThePopup(messageListener?: MessageListener) {
} }
//load video times for this video //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 != undefined && sponsorTimesStorage.length > 0) {
if (sponsorTimesStorage[sponsorTimesStorage.length - 1] != undefined && sponsorTimesStorage[sponsorTimesStorage.length - 1].segment.length < 2) { if (sponsorTimesStorage[sponsorTimesStorage.length - 1] != undefined && sponsorTimesStorage[sponsorTimesStorage.length - 1].segment.length < 2) {
startTimeChosen = true; startTimeChosen = true;
@@ -322,7 +322,7 @@ async function runThePopup(messageListener?: MessageListener) {
} }
function startSponsorCallback(response) { function startSponsorCallback(response) {
let sponsorTimesIndex = sponsorTimes.length - (startTimeChosen ? 1 : 0); const sponsorTimesIndex = sponsorTimes.length - (startTimeChosen ? 1 : 0);
if (sponsorTimes[sponsorTimesIndex] == undefined) { if (sponsorTimes[sponsorTimesIndex] == undefined) {
sponsorTimes[sponsorTimesIndex] = { sponsorTimes[sponsorTimesIndex] = {
@@ -334,7 +334,7 @@ async function runThePopup(messageListener?: MessageListener) {
sponsorTimes[sponsorTimesIndex].segment[startTimeChosen ? 1 : 0] = response.time; sponsorTimes[sponsorTimesIndex].segment[startTimeChosen ? 1 : 0] = response.time;
let localStartTimeChosen = startTimeChosen; const localStartTimeChosen = startTimeChosen;
Config.config.segmentTimes.set(currentVideoID, sponsorTimes); Config.config.segmentTimes.set(currentVideoID, sponsorTimes);
//send a message to the client script //send a message to the client script
@@ -363,19 +363,19 @@ async function runThePopup(messageListener?: MessageListener) {
if (request.sponsorTimes != undefined) { if (request.sponsorTimes != undefined) {
// Sort list by start time // 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[1] - b.segment[1])
.sort((a, b) => a.segment[0] - b.segment[0]); .sort((a, b) => a.segment[0] - b.segment[0]);
//add them as buttons to the issue reporting container //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++) { 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"; sponsorTimeButton.className = "segmentTimeButton popupElement";
let prefix = chrome.i18n.getMessage("category_" + segmentTimes[i].category) + ": "; const prefix = chrome.i18n.getMessage("category_" + segmentTimes[i].category) + ": ";
let extraInfo = ""; let extraInfo = "";
if (segmentTimes[i].hidden === SponsorHideType.Downvoted) { 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; 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.id = "sponsorTimesCategoryColorCircle" + UUID;
categoryColorCircle.style.backgroundColor = Config.config.barTypes[segmentTimes[i].category].color; categoryColorCircle.style.backgroundColor = Config.config.barTypes[segmentTimes[i].category].color;
categoryColorCircle.classList.add("dot"); categoryColorCircle.classList.add("dot");
categoryColorCircle.classList.add("sponsorTimesCategoryColorCircle"); categoryColorCircle.classList.add("sponsorTimesCategoryColorCircle");
let votingButtons = document.createElement("div"); const votingButtons = document.createElement("div");
votingButtons.classList.add("votingButtons"); votingButtons.classList.add("votingButtons");
//thumbs up and down buttons //thumbs up and down buttons
let voteButtonsContainer = document.createElement("div"); const voteButtonsContainer = document.createElement("div");
voteButtonsContainer.id = "sponsorTimesVoteButtonsContainer" + UUID; voteButtonsContainer.id = "sponsorTimesVoteButtonsContainer" + UUID;
voteButtonsContainer.setAttribute("align", "center"); voteButtonsContainer.setAttribute("align", "center");
voteButtonsContainer.style.display = "none" voteButtonsContainer.style.display = "none"
let upvoteButton = document.createElement("img"); const upvoteButton = document.createElement("img");
upvoteButton.id = "sponsorTimesUpvoteButtonsContainer" + UUID; upvoteButton.id = "sponsorTimesUpvoteButtonsContainer" + UUID;
upvoteButton.className = "voteButton"; upvoteButton.className = "voteButton";
upvoteButton.src = chrome.extension.getURL("icons/thumbs_up.svg"); upvoteButton.src = chrome.extension.getURL("icons/thumbs_up.svg");
upvoteButton.addEventListener("click", () => vote(1, UUID)); upvoteButton.addEventListener("click", () => vote(1, UUID));
let downvoteButton = document.createElement("img"); const downvoteButton = document.createElement("img");
downvoteButton.id = "sponsorTimesDownvoteButtonsContainer" + UUID; downvoteButton.id = "sponsorTimesDownvoteButtonsContainer" + UUID;
downvoteButton.className = "voteButton"; downvoteButton.className = "voteButton";
downvoteButton.src = chrome.extension.getURL("icons/thumbs_down.svg"); downvoteButton.src = chrome.extension.getURL("icons/thumbs_down.svg");
@@ -425,12 +425,12 @@ async function runThePopup(messageListener?: MessageListener) {
}); });
// Will contain request status // Will contain request status
let voteStatusContainer = document.createElement("div"); const voteStatusContainer = document.createElement("div");
voteStatusContainer.id = "sponsorTimesVoteStatusContainer" + UUID; voteStatusContainer.id = "sponsorTimesVoteStatusContainer" + UUID;
voteStatusContainer.classList.add("sponsorTimesVoteStatusContainer"); voteStatusContainer.classList.add("sponsorTimesVoteStatusContainer");
voteStatusContainer.style.display = "none"; voteStatusContainer.style.display = "none";
let thanksForVotingText = document.createElement("div"); const thanksForVotingText = document.createElement("div");
thanksForVotingText.id = "sponsorTimesThanksForVotingText" + UUID; thanksForVotingText.id = "sponsorTimesThanksForVotingText" + UUID;
thanksForVotingText.classList.add("sponsorTimesThanksForVotingText"); thanksForVotingText.classList.add("sponsorTimesThanksForVotingText");
voteStatusContainer.appendChild(thanksForVotingText); voteStatusContainer.appendChild(thanksForVotingText);
@@ -553,13 +553,13 @@ async function runThePopup(messageListener?: MessageListener) {
} }
function addVoteMessage(message, UUID) { function addVoteMessage(message, UUID) {
let voteButtonsContainer = document.getElementById("sponsorTimesVoteButtonsContainer" + UUID); const voteButtonsContainer = document.getElementById("sponsorTimesVoteButtonsContainer" + UUID);
voteButtonsContainer.style.display = "none"; voteButtonsContainer.style.display = "none";
let voteStatusContainer = document.getElementById("sponsorTimesVoteStatusContainer" + UUID); const voteStatusContainer = document.getElementById("sponsorTimesVoteStatusContainer" + UUID);
voteStatusContainer.style.removeProperty("display"); voteStatusContainer.style.removeProperty("display");
let thanksForVotingText = document.getElementById("sponsorTimesThanksForVotingText" + UUID); const thanksForVotingText = document.getElementById("sponsorTimesThanksForVotingText" + UUID);
thanksForVotingText.innerText = message; thanksForVotingText.innerText = message;
} }
@@ -587,15 +587,15 @@ async function runThePopup(messageListener?: MessageListener) {
//converts time in seconds to minutes:seconds //converts time in seconds to minutes:seconds
function getFormattedTime(seconds) { function getFormattedTime(seconds) {
let minutes = Math.floor(seconds / 60); const minutes = Math.floor(seconds / 60);
let secondsDisplayNumber = Math.round(seconds - minutes * 60); const secondsDisplayNumber = Math.round(seconds - minutes * 60);
let secondsDisplay = String(secondsDisplayNumber); let secondsDisplay = String(secondsDisplayNumber);
if (secondsDisplayNumber < 10) { if (secondsDisplayNumber < 10) {
//add a zero //add a zero
secondsDisplay = "0" + secondsDisplay; secondsDisplay = "0" + secondsDisplay;
} }
let formatted = minutes + ":" + secondsDisplay; const formatted = minutes + ":" + secondsDisplay;
return formatted; return formatted;
} }
@@ -669,7 +669,7 @@ async function runThePopup(messageListener?: MessageListener) {
} }
//remove this channel //remove this channel
let index = whitelistedChannels.indexOf(response.channelID); const index = whitelistedChannels.indexOf(response.channelID);
whitelistedChannels.splice(index, 1); whitelistedChannels.splice(index, 1);
//change button //change button
@@ -717,14 +717,14 @@ async function runThePopup(messageListener?: MessageListener) {
//converts time in seconds to minutes //converts time in seconds to minutes
function getTimeInMinutes(seconds) { function getTimeInMinutes(seconds) {
let minutes = Math.floor(seconds / 60); const minutes = Math.floor(seconds / 60);
return minutes; return minutes;
} }
//converts time in seconds to seconds past the last minute //converts time in seconds to seconds past the last minute
function getTimeInFormattedSeconds(seconds) { function getTimeInFormattedSeconds(seconds) {
let minutes = seconds % 60; const minutes = seconds % 60;
let secondsFormatted = minutes.toFixed(3); let secondsFormatted = minutes.toFixed(3);
if (minutes < 10) { if (minutes < 10) {
@@ -742,7 +742,7 @@ async function runThePopup(messageListener?: MessageListener) {
* @returns {string} * @returns {string}
*/ */
function getFormattedHours(minues) { function getFormattedHours(minues) {
let hours = Math.floor(minues / 60); const hours = Math.floor(minues / 60);
return (hours > 0 ? hours + "h " : "") + (minues % 60).toFixed(1); return (hours > 0 ? hours + "h " : "") + (minues % 60).toFixed(1);
} }

View File

@@ -14,7 +14,7 @@ class SkipNotice {
skipNoticeRef: React.MutableRefObject<SkipNoticeComponent>; skipNoticeRef: React.MutableRefObject<SkipNoticeComponent>;
constructor(segments: SponsorTime[], autoSkip: boolean = false, contentContainer: ContentContainer) { constructor(segments: SponsorTime[], autoSkip = false, contentContainer: ContentContainer) {
this.segments = segments; this.segments = segments;
this.autoSkip = autoSkip; this.autoSkip = autoSkip;
this.contentContainer = contentContainer; this.contentContainer = contentContainer;
@@ -24,7 +24,7 @@ class SkipNotice {
|| document.getElementById("movie_player") || document.querySelector("#player-container .video-js"); || document.getElementById("movie_player") || document.querySelector("#player-container .video-js");
if (referenceNode == null) { if (referenceNode == null) {
//for embeds //for embeds
let player = document.getElementById("player"); const player = document.getElementById("player");
referenceNode = player.firstChild as HTMLElement; referenceNode = player.firstChild as HTMLElement;
let index = 1; let index = 1;
@@ -40,7 +40,7 @@ class SkipNotice {
referenceNode = document.querySelector("#main-panel.ytmusic-player-page"); 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 //this is the suffix added at the end of every id
let idSuffix = ""; let idSuffix = "";
for (const segment of this.segments) { for (const segment of this.segments) {
@@ -63,7 +63,7 @@ class SkipNotice {
); );
} }
close() { close(): void {
ReactDOM.unmountComponentAtNode(this.noticeElement); ReactDOM.unmountComponentAtNode(this.noticeElement);
this.noticeElement.remove(); this.noticeElement.remove();

View File

@@ -2,18 +2,19 @@ import * as React from "react";
import * as ReactDOM from "react-dom"; import * as ReactDOM from "react-dom";
import SubmissionNoticeComponent from "../components/SubmissionNoticeComponent"; import SubmissionNoticeComponent from "../components/SubmissionNoticeComponent";
import { ContentContainer } from "../types";
class SubmissionNotice { class SubmissionNotice {
// Contains functions and variables from the content script needed by the skip notice // 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>; noticeRef: React.MutableRefObject<SubmissionNoticeComponent>;
noticeElement: HTMLDivElement; noticeElement: HTMLDivElement;
constructor(contentContainer: () => any, callback: () => any) { constructor(contentContainer: ContentContainer, callback: () => unknown) {
this.noticeRef = React.createRef(); this.noticeRef = React.createRef();
this.contentContainer = contentContainer; this.contentContainer = contentContainer;
@@ -24,7 +25,7 @@ class SubmissionNotice {
|| document.getElementById("movie_player") || document.querySelector("#player-container .video-js"); || document.getElementById("movie_player") || document.querySelector("#player-container .video-js");
if (referenceNode == null) { if (referenceNode == null) {
//for embeds //for embeds
let player = document.getElementById("player"); const player = document.getElementById("player");
referenceNode = player.firstChild as HTMLElement; referenceNode = player.firstChild as HTMLElement;
let index = 1; let index = 1;
@@ -51,11 +52,11 @@ class SubmissionNotice {
); );
} }
update() { update(): void {
this.noticeRef.current.forceUpdate(); this.noticeRef.current.forceUpdate();
} }
close() { close(): void {
ReactDOM.unmountComponentAtNode(this.noticeElement); ReactDOM.unmountComponentAtNode(this.noticeElement);
this.noticeElement.remove(); this.noticeElement.remove();

View File

@@ -63,6 +63,21 @@ interface PreviewBarOption {
opacity: string 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; type VideoID = string;
export { export {
@@ -74,5 +89,7 @@ export {
SponsorTime, SponsorTime,
VideoID, VideoID,
SponsorHideType, SponsorHideType,
PreviewBarOption PreviewBarOption,
Registration,
BackgroundScriptContainer
}; };

View File

@@ -1,12 +1,12 @@
import Config from "./config"; import Config from "./config";
import { CategorySelection, SponsorTime, FetchResponse } from "./types"; import { CategorySelection, SponsorTime, FetchResponse, BackgroundScriptContainer, Registration } from "./types";
import * as CompileConfig from "../config.json"; import * as CompileConfig from "../config.json";
class Utils { class Utils {
// Contains functions needed from the background script // Contains functions needed from the background script
backgroundScriptContainer: any = null; backgroundScriptContainer: BackgroundScriptContainer | null = null;
// Used to add content scripts and CSS required // Used to add content scripts and CSS required
js = [ js = [
@@ -19,24 +19,24 @@ class Utils {
"popup.css" "popup.css"
]; ];
constructor(backgroundScriptContainer?: any) { constructor(backgroundScriptContainer?: BackgroundScriptContainer) {
this.backgroundScriptContainer = backgroundScriptContainer; this.backgroundScriptContainer = backgroundScriptContainer;
} }
// Function that can be used to wait for a condition before returning // 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) => { return await new Promise((resolve, reject) => {
setTimeout(() => reject("TIMEOUT"), timeout); setTimeout(() => reject("TIMEOUT"), timeout);
let intervalCheck = () => { const intervalCheck = () => {
let result = condition(); const result = condition();
if (result !== false) { if (result !== false) {
resolve(result); resolve(result);
clearInterval(interval); clearInterval(interval);
}; }
}; };
let interval = setInterval(intervalCheck, check); const interval = setInterval(intervalCheck, check);
//run the check once first, this speeds it up a lot //run the check once first, this speeds it up a lot
intervalCheck(); intervalCheck();
@@ -51,12 +51,12 @@ class Utils {
* *
* @param {CallableFunction} callback * @param {CallableFunction} callback
*/ */
setupExtraSitePermissions(callback) { setupExtraSitePermissions(callback: (granted: boolean) => void): void {
// Request permission // Request permission
let permissions = ["declarativeContent"]; let permissions = ["declarativeContent"];
if (this.isFirefox()) permissions = []; if (this.isFirefox()) permissions = [];
let self = this; const self = this;
chrome.permissions.request({ chrome.permissions.request({
origins: this.getInvidiousInstancesRegex(), origins: this.getInvidiousInstancesRegex(),
@@ -79,20 +79,20 @@ class Utils {
* *
* For now, it is just SB.config.invidiousInstances. * For now, it is just SB.config.invidiousInstances.
*/ */
setupExtraSiteContentScripts() { setupExtraSiteContentScripts(): void {
let self = this; const self = this;
if (this.isFirefox()) { if (this.isFirefox()) {
let firefoxJS = []; const firefoxJS = [];
for (const file of this.js) { for (const file of this.js) {
firefoxJS.push({file}); firefoxJS.push({file});
} }
let firefoxCSS = []; const firefoxCSS = [];
for (const file of this.css) { for (const file of this.css) {
firefoxCSS.push({file}); firefoxCSS.push({file});
} }
let registration = { const registration: Registration = {
message: "registerContentScript", message: "registerContentScript",
id: "invidious", id: "invidious",
allFrames: true, allFrames: true,
@@ -108,7 +108,7 @@ class Utils {
} }
} else { } else {
chrome.declarativeContent.onPageChanged.removeRules(["invidious"], function() { chrome.declarativeContent.onPageChanged.removeRules(["invidious"], function() {
let conditions = []; const conditions = [];
for (const regex of self.getInvidiousInstancesRegex()) { for (const regex of self.getInvidiousInstancesRegex()) {
conditions.push(new chrome.declarativeContent.PageStateMatcher({ conditions.push(new chrome.declarativeContent.PageStateMatcher({
pageUrl: { urlMatches: regex } pageUrl: { urlMatches: regex }
@@ -116,7 +116,7 @@ class Utils {
} }
// Add page rule // Add page rule
let rule = { const rule = {
id: "invidious", id: "invidious",
conditions, conditions,
// This API is experimental and not visible by the TypeScript compiler // This API is experimental and not visible by the TypeScript compiler
@@ -135,9 +135,9 @@ class Utils {
/** /**
* Removes the permission and content script registration. * Removes the permission and content script registration.
*/ */
removeExtraSiteRegistration() { removeExtraSiteRegistration(): void {
if (this.isFirefox()) { if (this.isFirefox()) {
let id = "invidious"; const id = "invidious";
if (this.backgroundScriptContainer) { if (this.backgroundScriptContainer) {
this.backgroundScriptContainer.unregisterFirefoxContentScript(id); this.backgroundScriptContainer.unregisterFirefoxContentScript(id);
@@ -163,7 +163,7 @@ class Utils {
* @param sponsorTimes * @param sponsorTimes
*/ */
getSegmentsFromSponsorTimes(sponsorTimes: SponsorTime[]): number[][] { getSegmentsFromSponsorTimes(sponsorTimes: SponsorTime[]): number[][] {
let segments: number[][] = []; const segments: number[][] = [];
for (const sponsorTime of sponsorTimes) { for (const sponsorTime of sponsorTimes) {
segments.push(sponsorTime.segment); segments.push(sponsorTime.segment);
} }
@@ -193,19 +193,19 @@ class Utils {
} }
} }
localizeHtmlPage() { localizeHtmlPage(): void {
//Localize by replacing __MSG_***__ meta tags //Localize by replacing __MSG_***__ meta tags
var objects = document.getElementsByClassName("sponsorBlockPageBody")[0].children; const objects = document.getElementsByClassName("sponsorBlockPageBody")[0].children;
for (var j = 0; j < objects.length; j++) { for (let j = 0; j < objects.length; j++) {
var obj = objects[j]; const obj = objects[j];
let localizedMessage = this.getLocalizedMessage(obj.innerHTML.toString()); const localizedMessage = this.getLocalizedMessage(obj.innerHTML.toString());
if (localizedMessage) obj.innerHTML = localizedMessage; if (localizedMessage) obj.innerHTML = localizedMessage;
} }
} }
getLocalizedMessage(text) { getLocalizedMessage(text: string): string | false {
var valNewH = text.replace(/__MSG_(\w+)__/g, function(match, v1) { const valNewH = text.replace(/__MSG_(\w+)__/g, function(match, v1) {
return v1 ? chrome.i18n.getMessage(v1) : ""; return v1 ? chrome.i18n.getMessage(v1) : "";
}); });
@@ -219,8 +219,8 @@ class Utils {
/** /**
* @returns {String[]} Invidious Instances in regex form * @returns {String[]} Invidious Instances in regex form
*/ */
getInvidiousInstancesRegex() { getInvidiousInstancesRegex(): string[] {
var invidiousInstancesRegex = []; const invidiousInstancesRegex: string[] = [];
for (const url of Config.config.invidiousInstances) { for (const url of Config.config.invidiousInstances) {
invidiousInstancesRegex.push("https://*." + url + "/*"); invidiousInstancesRegex.push("https://*." + url + "/*");
invidiousInstancesRegex.push("http://*." + url + "/*"); invidiousInstancesRegex.push("http://*." + url + "/*");
@@ -229,11 +229,11 @@ class Utils {
return invidiousInstancesRegex; return invidiousInstancesRegex;
} }
generateUserID(length = 36) { generateUserID(length = 36): string {
let charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let result = ""; let result = "";
if (window.crypto && window.crypto.getRandomValues) { if (window.crypto && window.crypto.getRandomValues) {
let values = new Uint32Array(length); const values = new Uint32Array(length);
window.crypto.getRandomValues(values); window.crypto.getRandomValues(values);
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
result += charset[values[i] % charset.length]; result += charset[values[i] % charset.length];
@@ -253,7 +253,7 @@ class Utils {
* @param {int} statusCode * @param {int} statusCode
* @returns {string} errorMessage * @returns {string} errorMessage
*/ */
getErrorMessage(statusCode) { getErrorMessage(statusCode: number): string {
let errorMessage = ""; let errorMessage = "";
if([400, 429, 409, 502, 0].includes(statusCode)) { if([400, 429, 409, 502, 0].includes(statusCode)) {
@@ -298,7 +298,7 @@ class Utils {
* @param callback * @param callback
*/ */
async asyncRequestToServer(type: string, address: string, data = {}): Promise<FetchResponse> { 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)); 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 address The address to add to the SponsorBlock server address
* @param callback * @param callback
*/ */
sendRequestToServer(type: string, address: string, callback?: (response: FetchResponse) => void) { sendRequestToServer(type: string, address: string, callback?: (response: FetchResponse) => void): void {
let serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress; const serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
// Ask the background script to do the work // Ask the background script to do the work
chrome.runtime.sendMessage({ chrome.runtime.sendMessage({
@@ -324,15 +324,15 @@ class Utils {
} }
getFormattedTime(seconds: number, precise?: boolean): string { getFormattedTime(seconds: number, precise?: boolean): string {
let hours = Math.floor(seconds / 60 / 60); const hours = Math.floor(seconds / 60 / 60);
let minutes = Math.floor(seconds / 60) % 60; const minutes = Math.floor(seconds / 60) % 60;
let minutesDisplay = String(minutes); let minutesDisplay = String(minutes);
let secondsNum = seconds % 60; let secondsNum = seconds % 60;
if (!precise) { if (!precise) {
secondsNum = Math.floor(secondsNum); secondsNum = Math.floor(secondsNum);
} }
let secondsDisplay: string = String(precise ? secondsNum.toFixed(3) : secondsNum); let secondsDisplay = String(precise ? secondsNum.toFixed(3) : secondsNum);
if (secondsNum < 10) { if (secondsNum < 10) {
//add a zero //add a zero
@@ -343,7 +343,7 @@ class Utils {
minutesDisplay = "0" + minutesDisplay; minutesDisplay = "0" + minutesDisplay;
} }
let formatted = (hours ? hours + ":" : "") + minutesDisplay + ":" + secondsDisplay; const formatted = (hours ? hours + ":" : "") + minutesDisplay + ":" + secondsDisplay;
return formatted; return formatted;
} }