diff --git a/src/SB.ts b/src/SB.ts index 48edab13..9f816ae2 100644 --- a/src/SB.ts +++ b/src/SB.ts @@ -1,4 +1,3 @@ - interface SBObject { configListeners: Array; defaults: any; diff --git a/src/background.ts b/src/background.ts index 29f63c51..a5650135 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1,17 +1,19 @@ -import Utils from "./utils"; +import * as Types from "./types"; import SB from "./SB"; -import * as Types from "./types"; - -Utils.isBackgroundScript = true; +import Utils from "./utils"; +var utils = new Utils({ + registerFirefoxContentScript, + unregisterFirefoxContentScript +}); // Used only on Firefox, which does not support non persistent background pages. var contentScriptRegistrations = {}; // Register content script if needed -if (Utils.isFirefox()) { - Utils.wait(() => SB.config !== undefined).then(function() { - if (SB.config.supportInvidious) Utils.setupExtraSiteContentScripts(); +if (utils.isFirefox()) { + utils.wait(() => SB.config !== undefined).then(function() { + if (SB.config.supportInvidious) utils.setupExtraSiteContentScripts(); }); } @@ -62,9 +64,7 @@ chrome.runtime.onMessage.addListener(function (request, sender, callback) { registerFirefoxContentScript(request); return false; case "unregisterContentScript": - contentScriptRegistrations[request.id].unregister(); - delete contentScriptRegistrations[request.id]; - + unregisterFirefoxContentScript(request.id) return false; } }); @@ -82,7 +82,7 @@ chrome.runtime.onInstalled.addListener(function (object) { chrome.tabs.create({url: chrome.extension.getURL("/help/index_en.html")}); //generate a userID - const newUserID = Utils.generateUserID(); + const newUserID = utils.generateUserID(); //save this UUID SB.config.userID = newUserID; @@ -111,6 +111,16 @@ function registerFirefoxContentScript(options) { }).then((registration) => void (contentScriptRegistrations[options.id] = registration)); } +/** + * Only works on Firefox. + * Firefox requires that this is handled by the background script + * + */ +function unregisterFirefoxContentScript(id: string) { + contentScriptRegistrations[id].unregister(); + delete contentScriptRegistrations[id]; +} + //gets the sponsor times from memory function getSponsorTimes(videoID, callback) { let sponsorTimes = []; @@ -148,12 +158,12 @@ function submitVote(type, UUID, callback) { if (userID == undefined || userID === "undefined") { //generate one - userID = Utils.generateUserID(); + userID = utils.generateUserID(); SB.config.userID = userID; } //publish this vote - Utils.sendRequestToServer("POST", "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + "&type=" + type, function(xmlhttp, error) { + utils.sendRequestToServer("POST", "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + "&type=" + type, function(xmlhttp, error) { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { callback({ successType: 1 @@ -205,7 +215,7 @@ async function submitTimes(videoID, callback) { let increasedContributionAmount = false; //submit the sponsorTime - Utils.sendRequestToServer("GET", "/api/postVideoSponsorTimes?videoID=" + videoID + "&startTime=" + sponsorTimes[i][0] + "&endTime=" + sponsorTimes[i][1] + utils.sendRequestToServer("GET", "/api/postVideoSponsorTimes?videoID=" + videoID + "&startTime=" + sponsorTimes[i][0] + "&endTime=" + sponsorTimes[i][1] + "&userID=" + userID, function(xmlhttp, error) { if (xmlhttp.readyState == 4 && !error) { callback({ diff --git a/src/content.ts b/src/content.ts index 06d5fba6..ed53100c 100644 --- a/src/content.ts +++ b/src/content.ts @@ -1,7 +1,9 @@ -import Utils from "./utils"; import SB from "./SB"; -import runThePopup from "./popup.js"; +import Utils from "./utils"; +var utils = new Utils(); + +import runThePopup from "./popup"; import PreviewBar from "./js-components/previewBar"; import SkipNotice from "./js-components/skipNotice"; @@ -24,7 +26,7 @@ var sponsorSkipped = []; //the video var v; -var listenerAdded; +var onInvidious; //the video id of the last preview bar update var lastPreviewBarUpdate; @@ -48,7 +50,7 @@ var previewBar = null; var controls = null; // Direct Links -videoIDChange(Utils.getYouTubeVideoID(document.URL)); +videoIDChange(getYouTubeVideoID(document.URL)); //the last time looked at (used to see if this time is in the interval) var lastTime = -1; @@ -93,7 +95,7 @@ function messageListener(request: any, sender: any, sendResponse: (response: any //messages from popup script switch(request.message){ case "update": - videoIDChange(Utils.getYouTubeVideoID(document.URL)); + videoIDChange(getYouTubeVideoID(document.URL)); break; case "sponsorStart": sponsorMessageStarted(sendResponse); @@ -239,13 +241,13 @@ function videoIDChange(id) { // TODO: Use a better method here than using type any // This is done to be able to do channelIDPromise.isFulfilled and channelIDPromise.isRejected - let channelIDPromise: any = Utils.wait(getChannelID); + let channelIDPromise: any = utils.wait(getChannelID); channelIDPromise.then(() => channelIDPromise.isFulfilled = true).catch(() => channelIDPromise.isRejected = true); //setup the preview bar if (previewBar == null) { //create it - Utils.wait(getControls).then(result => { + utils.wait(getControls).then(result => { const progressElementSelectors = [ // For YouTube "ytp-progress-bar-container", @@ -296,7 +298,7 @@ function videoIDChange(id) { sponsorTimesSubmitting = []; //see if the onvideo control image needs to be changed - Utils.wait(getControls).then(result => { + utils.wait(getControls).then(result => { chrome.runtime.sendMessage({ message: "getSponsorTimes", videoID: id @@ -321,7 +323,7 @@ function videoIDChange(id) { }); }); //see if video controls buttons should be added - if (!Utils.onInvidious) { + if (!onInvidious) { updateVisibilityOfPlayerControlsButton(); } } @@ -346,7 +348,7 @@ function sponsorsLookup(id: string, channelIDPromise = null) { whitelistCheck(); } else if (channelIDPromise.isRejected) { //try again - Utils.wait(getChannelID).then(whitelistCheck).catch(); + utils.wait(getChannelID).then(whitelistCheck).catch(); } else { //add it as a then statement channelIDPromise.then(whitelistCheck); @@ -356,7 +358,7 @@ function sponsorsLookup(id: string, channelIDPromise = null) { //check database for sponsor times //made true once a setTimeout has been created to try again after a server error let recheckStarted = false; - Utils.sendRequestToServer('GET', "/api/getVideoSponsorTimes?videoID=" + id, function(xmlhttp) { + utils.sendRequestToServer('GET', "/api/getVideoSponsorTimes?videoID=" + id, function(xmlhttp) { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { sponsorDataFound = true; @@ -412,30 +414,44 @@ function sponsorsLookup(id: string, channelIDPromise = null) { } } -function updatePreviewBar() { - let localSponsorTimes = sponsorTimes; - if (localSponsorTimes == null) localSponsorTimes = []; +function getYouTubeVideoID(url: string) { + // For YouTube TV support + if(url.startsWith("https://www.youtube.com/tv#/")) url = url.replace("#", ""); + + //Attempt to parse url + let urlObject = null; + try { + urlObject = new URL(url); + } catch (e) { + console.error("[SB] Unable to parse URL: " + url); + return false; + } - let allSponsorTimes = localSponsorTimes.concat(sponsorTimesSubmitting); - - //create an array of the sponsor types - let types = []; - for (let i = 0; i < localSponsorTimes.length; i++) { - if (!hiddenSponsorTimes.includes(i)) { - types.push("sponsor"); - } else { - // Don't show this sponsor - types.push(null); + //Check if valid hostname + if (SB.config && SB.config.invidiousInstances.includes(urlObject.host)) { + onInvidious = true; + } else if (!["www.youtube.com", "www.youtube-nocookie.com"].includes(urlObject.host)) { + if (!SB.config) { + // Call this later, in case this is an Invidious tab + this.wait(() => SB.config !== undefined).then(() => this.videoIDChange(this.getYouTubeVideoID(url))); } - } - for (let i = 0; i < sponsorTimesSubmitting.length; i++) { - types.push("previewSponsor"); + + return false } - Utils.wait(() => previewBar !== null).then((result) => previewBar.set(allSponsorTimes, types, v.duration)); - - //update last video id - lastPreviewBarUpdate = sponsorVideoID; + //Get ID from searchParam + if (urlObject.searchParams.has("v") && ["/watch", "/watch/"].includes(urlObject.pathname) || urlObject.pathname.startsWith("/tv/watch")) { + let id = urlObject.searchParams.get("v"); + return id.length == 11 ? id : false; + } else if (urlObject.pathname.startsWith("/embed/")) { + try { + return urlObject.pathname.substr(7, 11); + } catch (e) { + console.error("[SB] Video ID not valid for " + url); + return false; + } + } + return false; } function getChannelID() { @@ -445,7 +461,7 @@ function getChannelID() { channelURLContainer = document.querySelector("#channel-name > #container > #text-container > #text"); if (channelURLContainer !== null) { channelURLContainer = channelURLContainer.firstElementChild; - } else if (Utils.onInvidious) { + } else if (onInvidious) { // Unfortunately, the Invidious HTML doesn't have much in the way of element identifiers... channelURLContainer = document.querySelector("body > div > div.pure-u-1.pure-u-md-20-24 div.pure-u-1.pure-u-lg-3-5 > div > a"); } else { @@ -466,7 +482,7 @@ function getChannelID() { let currentTitle = ""; if (titleInfoContainer != null) { currentTitle = ( titleInfoContainer.firstElementChild.firstElementChild.querySelector(".title").firstElementChild).innerText; - } else if (Utils.onInvidious) { + } else if (onInvidious) { // Unfortunately, the Invidious HTML doesn't have much in the way of element identifiers... currentTitle = document.querySelector("body > div > div.pure-u-1.pure-u-md-20-24 div.pure-u-1.pure-u-lg-3-5 > div > a > div > span").textContent; } else { @@ -487,6 +503,32 @@ function getChannelID() { channelWhitelisted = false; } +function updatePreviewBar() { + let localSponsorTimes = sponsorTimes; + if (localSponsorTimes == null) localSponsorTimes = []; + + let allSponsorTimes = localSponsorTimes.concat(sponsorTimesSubmitting); + + //create an array of the sponsor types + let types = []; + for (let i = 0; i < localSponsorTimes.length; i++) { + if (!hiddenSponsorTimes.includes(i)) { + types.push("sponsor"); + } else { + // Don't show this sponsor + types.push(null); + } + } + for (let i = 0; i < sponsorTimesSubmitting.length; i++) { + types.push("previewSponsor"); + } + + utils.wait(() => previewBar !== null).then((result) => previewBar.set(allSponsorTimes, types, v.duration)); + + //update last video id + lastPreviewBarUpdate = sponsorVideoID; +} + //checks if this channel is whitelisted, should be done only after the channelID has been loaded function whitelistCheck() { //see if this is a whitelisted channel @@ -595,7 +637,7 @@ function skipToTime(v, index, sponsorTimes, openNotice) { //send telemetry that a this sponsor was skipped if (SB.config.trackViewCount && !sponsorSkipped[index]) { - Utils.sendRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + currentUUID); + utils.sendRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + currentUUID); if (!SB.config.disableAutoSkip) { // Count this as a skip @@ -659,7 +701,7 @@ function getControls() { //adds all the player controls buttons async function createButtons() { - let result = await Utils.wait(getControls).catch(); + let result = await utils.wait(getControls).catch(); //set global controls variable controls = result; @@ -677,7 +719,7 @@ async function updateVisibilityOfPlayerControlsButton() { await createButtons(); - if (SB.config.hideVideoPlayerControls || Utils.onInvidious) { + if (SB.config.hideVideoPlayerControls || onInvidious) { document.getElementById("startSponsorButton").style.display = "none"; document.getElementById("submitButton").style.display = "none"; } else { @@ -685,13 +727,13 @@ async function updateVisibilityOfPlayerControlsButton() { } //don't show the info button on embeds - if (SB.config.hideInfoButtonPlayerControls || document.URL.includes("/embed/") || Utils.onInvidious) { + if (SB.config.hideInfoButtonPlayerControls || document.URL.includes("/embed/") || onInvidious) { document.getElementById("infoButton").style.display = "none"; } else { document.getElementById("infoButton").style.removeProperty("display"); } - if (SB.config.hideDeleteButtonPlayerControls || Utils.onInvidious) { + if (SB.config.hideDeleteButtonPlayerControls || onInvidious) { document.getElementById("deleteButton").style.display = "none"; } } @@ -740,10 +782,10 @@ async function changeStartSponsorButton(showStartSponsor, uploadButtonVisible) { if(!sponsorVideoID) return false; //make sure submit button is loaded - await Utils.wait(isSubmitButtonLoaded); + await utils.wait(isSubmitButtonLoaded); //if it isn't visible, there is no data - let shouldHide = (uploadButtonVisible && !(SB.config.hideDeleteButtonPlayerControls || Utils.onInvidious)) ? "unset" : "none" + let shouldHide = (uploadButtonVisible && !(SB.config.hideDeleteButtonPlayerControls || onInvidious)) ? "unset" : "none" document.getElementById("deleteButton").style.display = shouldHide; if (showStartSponsor) { @@ -911,7 +953,7 @@ function vote(type, UUID, skipNotice) { skipNotice.addNoticeInfoMessage.bind(skipNotice)(chrome.i18n.getMessage("voteFail")) skipNotice.resetVoteButtonInfo.bind(skipNotice)(); } else if (response.successType == -1) { - skipNotice.addNoticeInfoMessage.bind(skipNotice)(Utils.getErrorMessage(response.statusCode)) + skipNotice.addNoticeInfoMessage.bind(skipNotice)(utils.getErrorMessage(response.statusCode)) skipNotice.resetVoteButtonInfo.bind(skipNotice)(); } } @@ -1016,7 +1058,7 @@ function sendSubmitMessage(){ sponsorTimes = sponsorTimes.concat(sponsorTimesSubmitting); for (let i = 0; i < sponsorTimesSubmitting.length; i++) { // Add some random IDs - UUIDs.push(Utils.generateUserID()); + UUIDs.push(utils.generateUserID()); } // Empty the submitting times @@ -1028,7 +1070,7 @@ function sendSubmitMessage(){ document.getElementById("submitButton").style.animation = "unset"; ( document.getElementById("submitImage")).src = chrome.extension.getURL("icons/PlayerUploadFailedIconSponsorBlocker256px.png"); - alert(Utils.getErrorMessage(response.statusCode)); + alert(utils.getErrorMessage(response.statusCode)); } } }); diff --git a/src/popup.ts b/src/popup.ts index a2c46c5f..f53aded2 100644 --- a/src/popup.ts +++ b/src/popup.ts @@ -1,6 +1,8 @@ -import Utils from "./utils"; import SB from "./SB"; +import Utils from "./utils"; +var utils = new Utils(); + interface MessageListener { (request: any, sender: any, callback: (response: any) => void): void; } @@ -38,9 +40,9 @@ class MessageHandler { async function runThePopup(messageListener?: MessageListener) { var messageHandler = new MessageHandler(); - Utils.localizeHtmlPage(); + utils.localizeHtmlPage(); - await Utils.wait(() => SB.config !== undefined); + await utils.wait(() => SB.config !== undefined); var OptionsElements: any = {}; @@ -168,7 +170,7 @@ async function runThePopup(messageListener?: MessageListener) { if (userID != undefined) { //there are probably some views on these submissions then //get the amount of views from the sponsors submitted - Utils.sendRequestToServer("GET", "/api/getViewsForUser?userID=" + userID, function(xmlhttp) { + utils.sendRequestToServer("GET", "/api/getViewsForUser?userID=" + userID, function(xmlhttp) { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { let viewCount = JSON.parse(xmlhttp.responseText).viewCount; if (viewCount != 0) { @@ -185,7 +187,7 @@ async function runThePopup(messageListener?: MessageListener) { }); //get this time in minutes - Utils.sendRequestToServer("GET", "/api/getSavedTimeForUser?userID=" + userID, function(xmlhttp) { + utils.sendRequestToServer("GET", "/api/getSavedTimeForUser?userID=" + userID, function(xmlhttp) { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { let minutesSaved = JSON.parse(xmlhttp.responseText).timeSaved; if (minutesSaved != 0) { @@ -802,7 +804,7 @@ async function runThePopup(messageListener?: MessageListener) { clearTimes(); } else { - document.getElementById("submitTimesInfoMessage").innerText = Utils.getErrorMessage(response.statusCode); + document.getElementById("submitTimesInfoMessage").innerText = utils.getErrorMessage(response.statusCode); document.getElementById("submitTimesInfoMessageContainer").style.display = "unset"; OptionsElements.submitTimesInfoMessageContainer.style.display = "unset"; @@ -854,7 +856,7 @@ async function runThePopup(messageListener?: MessageListener) { //make the options username setting option visible function setUsernameButton() { //get username from the server - Utils.sendRequestToServer("GET", "/api/getUsername?userID=" + SB.config.userID, function (xmlhttp, error) { + utils.sendRequestToServer("GET", "/api/getUsername?userID=" + SB.config.userID, function (xmlhttp, error) { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { OptionsElements.usernameInput.value = JSON.parse(xmlhttp.responseText).userName; @@ -871,7 +873,7 @@ async function runThePopup(messageListener?: MessageListener) { OptionsElements.usernameInput.style.display = "none"; OptionsElements.setUsernameStatusContainer.style.display = "unset"; - OptionsElements.setUsernameStatus.innerText = Utils.getErrorMessage(xmlhttp.status); + OptionsElements.setUsernameStatus.innerText = utils.getErrorMessage(xmlhttp.status); } }); } @@ -883,7 +885,7 @@ async function runThePopup(messageListener?: MessageListener) { OptionsElements.setUsernameStatus.innerText = "Loading..."; //get the userID - Utils.sendRequestToServer("POST", "/api/setUsername?userID=" + SB.config.userID + "&username=" + OptionsElements.usernameInput.value, function (xmlhttp, error) { + utils.sendRequestToServer("POST", "/api/setUsername?userID=" + SB.config.userID + "&username=" + OptionsElements.usernameInput.value, function (xmlhttp, error) { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { //submitted OptionsElements.submitUsername.style.display = "none"; @@ -891,7 +893,7 @@ async function runThePopup(messageListener?: MessageListener) { OptionsElements.setUsernameStatus.innerText = chrome.i18n.getMessage("success"); } else if (xmlhttp.readyState == 4) { - OptionsElements.setUsernameStatus.innerText = Utils.getErrorMessage(xmlhttp.status); + OptionsElements.setUsernameStatus.innerText = utils.getErrorMessage(xmlhttp.status); } }); @@ -944,7 +946,7 @@ async function runThePopup(messageListener?: MessageListener) { //failure: duplicate vote addVoteMessage(chrome.i18n.getMessage("voteFail"), UUID) } else if (response.successType == -1) { - addVoteMessage(Utils.getErrorMessage(response.statusCode), UUID) + addVoteMessage(utils.getErrorMessage(response.statusCode), UUID) } } }); diff --git a/src/utils.ts b/src/utils.ts index ad8bcfac..a73004d1 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -3,11 +3,15 @@ import SB from "./SB"; class Utils { - static isBackgroundScript = false; - static onInvidious = false; + // Contains functions needed from the background script + backgroundScriptContainer: any = null; + + constructor(backgroundScriptContainer?: any) { + this.backgroundScriptContainer = backgroundScriptContainer; + } // Function that can be used to wait for a condition before returning - static async wait(condition, timeout = 5000, check = 100) { + async wait(condition, timeout = 5000, check = 100) { return await new Promise((resolve, reject) => { setTimeout(() => reject("TIMEOUT"), timeout); @@ -26,46 +30,6 @@ class Utils { }); } - static getYouTubeVideoID(url: string) { - // For YouTube TV support - if(url.startsWith("https://www.youtube.com/tv#/")) url = url.replace("#", ""); - - //Attempt to parse url - let urlObject = null; - try { - urlObject = new URL(url); - } catch (e) { - console.error("[SB] Unable to parse URL: " + url); - return false; - } - - //Check if valid hostname - if (SB.config && SB.config.invidiousInstances.includes(urlObject.host)) { - onInvidious = true; - } else if (!["www.youtube.com", "www.youtube-nocookie.com"].includes(urlObject.host)) { - if (!SB.config) { - // Call this later, in case this is an Invidious tab - this.wait(() => SB.config !== undefined).then(() => this.videoIDChange(this.getYouTubeVideoID(url))); - } - - return false - } - - //Get ID from searchParam - if (urlObject.searchParams.has("v") && ["/watch", "/watch/"].includes(urlObject.pathname) || urlObject.pathname.startsWith("/tv/watch")) { - let id = urlObject.searchParams.get("v"); - return id.length == 11 ? id : false; - } else if (urlObject.pathname.startsWith("/embed/")) { - try { - return urlObject.pathname.substr(7, 11); - } catch (e) { - console.error("[SB] Video ID not valid for " + url); - return false; - } - } - return false; - } - /** * Asks for the optional permissions required for all extra sites. * It also starts the content script registrations. @@ -74,7 +38,7 @@ class Utils { * * @param {CallableFunction} callback */ - static setupExtraSitePermissions(callback) { + setupExtraSitePermissions(callback) { // Request permission let permissions = ["declarativeContent"]; if (this.isFirefox()) permissions = []; @@ -100,7 +64,7 @@ class Utils { * * For now, it is just SB.config.invidiousInstances. */ - static setupExtraSiteContentScripts() { + setupExtraSiteContentScripts() { let js = [ "config.js", "SB.js", @@ -135,8 +99,8 @@ class Utils { matches: this.getInvidiousInstancesRegex() }; - if (this.isBackgroundScript) { - registerFirefoxContentScript(registration); + if (this.backgroundScriptContainer) { + this.backgroundScriptContainer.registerFirefoxContentScript(registration); } else { chrome.runtime.sendMessage(registration); } @@ -169,15 +133,12 @@ class Utils { /** * Removes the permission and content script registration. */ - static removeExtraSiteRegistration() { + removeExtraSiteRegistration() { if (this.isFirefox()) { let id = "invidious"; - if (this.isBackgroundScript) { - if (contentScriptRegistrations[id]) { - contentScriptRegistrations[id].unregister(); - delete contentScriptRegistrations[id]; - } + if (this.backgroundScriptContainer) { + this.backgroundScriptContainer.unregisterFirefoxContentScript(id); } else { chrome.runtime.sendMessage({ message: "unregisterContentScript", @@ -193,7 +154,7 @@ class Utils { }); } - static localizeHtmlPage() { + localizeHtmlPage() { //Localize by replacing __MSG_***__ meta tags var objects = document.getElementsByClassName("sponsorBlockPageBody")[0].children; for (var j = 0; j < objects.length; j++) { @@ -204,7 +165,7 @@ class Utils { } } - static getLocalizedMessage(text) { + getLocalizedMessage(text) { var valNewH = text.replace(/__MSG_(\w+)__/g, function(match, v1) { return v1 ? chrome.i18n.getMessage(v1) : ""; }); @@ -219,7 +180,7 @@ class Utils { /** * @returns {String[]} Invidious Instances in regex form */ - static getInvidiousInstancesRegex() { + getInvidiousInstancesRegex() { var invidiousInstancesRegex = []; for (const url of SB.config.invidiousInstances) { invidiousInstancesRegex.push("https://*." + url + "/*"); @@ -229,7 +190,7 @@ class Utils { return invidiousInstancesRegex; } - static generateUserID(length = 36) { + generateUserID(length = 36) { let charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; let result = ""; if (window.crypto && window.crypto.getRandomValues) { @@ -253,7 +214,7 @@ class Utils { * @param {int} statusCode * @returns {string} errorMessage */ - static getErrorMessage(statusCode) { + getErrorMessage(statusCode) { let errorMessage = ""; if([400, 429, 409, 502, 0].includes(statusCode)) { @@ -276,7 +237,7 @@ class Utils { * @param address The address to add to the SponsorBlock server address * @param callback */ - static sendRequestToServer(type: string, address: string, callback?: (xmlhttp: XMLHttpRequest, err: boolean) => any) { + sendRequestToServer(type: string, address: string, callback?: (xmlhttp: XMLHttpRequest, err: boolean) => any) { let xmlhttp = new XMLHttpRequest(); xmlhttp.open(type, CompileConfig.serverAddress + address, true); @@ -298,7 +259,7 @@ class Utils { /** * Is this Firefox (web-extensions) */ - static isFirefox() { + isFirefox() { return typeof(browser) !== "undefined"; } }