diff --git a/config.json b/config.json
new file mode 100644
index 00000000..d5f188ed
--- /dev/null
+++ b/config.json
@@ -0,0 +1,3 @@
+{
+ "serverAddress": "http://localhost"
+}
\ No newline at end of file
diff --git a/config.json.example.json b/config.json.example.json
new file mode 100644
index 00000000..34c3d2a6
--- /dev/null
+++ b/config.json.example.json
@@ -0,0 +1,4 @@
+{
+ "serverAddress": "https://sponsor.ajay.app",
+ "serverAddressComment": "This specifies the default SponsorBlock server to conect to":
+}
\ No newline at end of file
diff --git a/public/popup.html b/public/popup.html
index 2c3c1f18..68e9cba2 100644
--- a/public/popup.html
+++ b/public/popup.html
@@ -2,8 +2,8 @@
__MSG_openPopup__
-
-
+
+
diff --git a/src/config.js.example b/src/config.js.example
deleted file mode 100644
index 23cc0be3..00000000
--- a/src/config.js.example
+++ /dev/null
@@ -1,3 +0,0 @@
-//this file is loaded along iwth content.js
-//this file sets the server to connect to, and is gitignored
-var serverAddress = "https://sponsor.ajay.app";
\ No newline at end of file
diff --git a/src/content.ts b/src/content.ts
index 8890329c..2e3f7c73 100644
--- a/src/content.ts
+++ b/src/content.ts
@@ -800,8 +800,8 @@ function openInfoMenu() {
logo.src = chrome.extension.getURL("icons/LogoSponsorBlocker256px.png");
//remove the style sheet and font that are not necessary
- popup.querySelector("#sponorBlockPopupFont").remove();
- popup.querySelector("#sponorBlockStyleSheet").remove();
+ popup.querySelector("#sponsorBlockPopupFont").remove();
+ popup.querySelector("#sponsorBlockStyleSheet").remove();
parentNode.insertBefore(popup, parentNode.firstChild);
diff --git a/src/popup.ts b/src/popup.ts
new file mode 100644
index 00000000..2c1b2ac7
--- /dev/null
+++ b/src/popup.ts
@@ -0,0 +1,1150 @@
+import * as CompileConfig from "../config.json";
+import Utils from "./utils";
+import SB from "./SB";
+
+class MessageHandler {
+ onContentScript: boolean;
+
+ constructor (onContentScript: boolean = false) {
+ this.onContentScript = onContentScript;
+ }
+
+ sendMessage(id: number, request, callback?) {
+ if (this.onContentScript) {
+ messageListener(request, null, callback);
+ } else {
+ chrome.tabs.sendMessage(id, request. callback);
+ }
+ }
+
+ query(config, callback) {
+ if (this.onContentScript) {
+ // Send back dummy info
+ callback([{
+ url: document.URL,
+ id: -1
+ }]);
+ } else {
+ chrome.tabs.query(config, callback);
+ }
+
+ }
+}
+
+var messageHandler = new MessageHandler();
+
+//make this a function to allow this to run on the content page
+async function runThePopup() {
+ Utils.localizeHtmlPage();
+
+ //is it in the popup or content script
+ var inPopup = true;
+ if (chrome.tabs == undefined) {
+ //this is on the content script, use direct communication
+ messageHandler = new MessageHandler(true);
+
+ inPopup = false;
+ }
+
+ await Utils.wait(() => SB.config !== undefined);
+
+ var OptionsElements: any = {};
+
+ ["sponsorStart",
+ // Top toggles
+ "whitelistChannel",
+ "unwhitelistChannel",
+ "disableSkipping",
+ "enableSkipping",
+ // Options
+ "showNoticeAgain",
+ "optionsButton",
+ // More controls
+ "clearTimes",
+ "submitTimes",
+ "reportAnIssue",
+ // sponsorTimesContributions
+ "sponsorTimesContributionsContainer",
+ "sponsorTimesContributionsDisplay",
+ "sponsorTimesContributionsDisplayEndWord",
+ // sponsorTimesViewsDisplay
+ "sponsorTimesViewsContainer",
+ "sponsorTimesViewsDisplay",
+ "sponsorTimesViewsDisplayEndWord",
+ // sponsorTimesOthersTimeSaved
+ "sponsorTimesOthersTimeSavedContainer",
+ "sponsorTimesOthersTimeSavedDisplay",
+ "sponsorTimesOthersTimeSavedEndWord",
+ // sponsorTimesSkipsDone
+ "sponsorTimesSkipsDoneContainer",
+ "sponsorTimesSkipsDoneDisplay",
+ "sponsorTimesSkipsDoneEndWord",
+ // sponsorTimeSaved
+ "sponsorTimeSavedContainer",
+ "sponsorTimeSavedDisplay",
+ "sponsorTimeSavedEndWord",
+ // discordButtons
+ "discordButtonContainer",
+ "hideDiscordButton",
+ // submitTimesInfoMessage
+ "submitTimesInfoMessageContainer",
+ "submitTimesInfoMessage",
+ // Username
+ "setUsernameContainer",
+ "setUsernameButton",
+ "setUsernameStatusContainer",
+ "setUsernameStatus",
+ "setUsername",
+ "usernameInput",
+ "submitUsername",
+ // More
+ "submissionSection",
+ "mainControls",
+ "loadingIndicator",
+ "videoFound",
+ "sponsorMessageTimes",
+ "downloadedSponsorMessageTimes",
+ ].forEach(id => OptionsElements[id] = document.getElementById(id));
+
+ //setup click listeners
+ OptionsElements.sponsorStart.addEventListener("click", sendSponsorStartMessage);
+ OptionsElements.whitelistChannel.addEventListener("click", whitelistChannel);
+ OptionsElements.unwhitelistChannel.addEventListener("click", unwhitelistChannel);
+ OptionsElements.disableSkipping.addEventListener("click", () => toggleSkipping(true));
+ OptionsElements.enableSkipping.addEventListener("click", () => toggleSkipping(false));
+ OptionsElements.clearTimes.addEventListener("click", clearTimes);
+ OptionsElements.submitTimes.addEventListener("click", submitTimes);
+ OptionsElements.showNoticeAgain.addEventListener("click", showNoticeAgain);
+ OptionsElements.setUsernameButton.addEventListener("click", setUsernameButton);
+ OptionsElements.submitUsername.addEventListener("click", submitUsername);
+ OptionsElements.optionsButton.addEventListener("click", openOptions);
+ OptionsElements.reportAnIssue.addEventListener("click", reportAnIssue);
+ OptionsElements.hideDiscordButton.addEventListener("click", hideDiscordButton);
+
+ //if true, the button now selects the end time
+ let startTimeChosen = false;
+
+ //the start and end time pairs (2d)
+ let sponsorTimes = [];
+
+ //current video ID of this tab
+ let currentVideoID = null;
+
+ //see if discord link can be shown
+ let hideDiscordLink = SB.config.hideDiscordLink;
+ if (hideDiscordLink == undefined || !hideDiscordLink) {
+ let hideDiscordLaunches = SB.config.hideDiscordLaunches;
+ //only if less than 10 launches
+ if (hideDiscordLaunches == undefined || hideDiscordLaunches < 10) {
+ OptionsElements.discordButtonContainer.style.display = null;
+
+ if (hideDiscordLaunches == undefined) {
+ hideDiscordLaunches = 1;
+ }
+ SB.config.hideDiscordLaunches = hideDiscordLaunches + 1;
+ }
+ }
+
+ //show proper disable skipping button
+ let disableSkipping = SB.config.disableSkipping;
+ if (disableSkipping != undefined && disableSkipping) {
+ OptionsElements.disableSkipping.style.display = "none";
+ OptionsElements.enableSkipping.style.display = "unset";
+ }
+
+ //if the don't show notice again variable is true, an option to
+ // disable should be available
+ let dontShowNotice = SB.config.dontShowNotice;
+ if (dontShowNotice != undefined && dontShowNotice) {
+ OptionsElements.showNoticeAgain.style.display = "unset";
+ }
+
+ //get the amount of times this user has contributed and display it to thank them
+ if (SB.config.sponsorTimesContributed != undefined) {
+ if (SB.config.sponsorTimesContributed > 1) {
+ OptionsElements.sponsorTimesContributionsDisplayEndWord.innerText = chrome.i18n.getMessage("Sponsors");
+ } else {
+ OptionsElements.sponsorTimesContributionsDisplayEndWord.innerText = chrome.i18n.getMessage("Sponsor");
+ }
+ OptionsElements.sponsorTimesContributionsDisplay.innerText = SB.config.sponsorTimesContributed;
+ OptionsElements.sponsorTimesContributionsContainer.style.display = "unset";
+
+ //get the userID
+ let userID = SB.config.userID;
+ if (userID != undefined) {
+ //there are probably some views on these submissions then
+ //get the amount of views from the sponsors submitted
+ 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) {
+ if (viewCount > 1) {
+ OptionsElements.sponsorTimesViewsDisplayEndWord.innerText = chrome.i18n.getMessage("Segments");
+ } else {
+ OptionsElements.sponsorTimesViewsDisplayEndWord.innerText = chrome.i18n.getMessage("Segment");
+ }
+
+ OptionsElements.sponsorTimesViewsDisplay.innerText = viewCount;
+ OptionsElements.sponsorTimesViewsContainer.style.display = "unset";
+ }
+ }
+ });
+
+ //get this time in minutes
+ 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) {
+ if (minutesSaved != 1) {
+ OptionsElements.sponsorTimesOthersTimeSavedEndWord.innerText = chrome.i18n.getMessage("minsLower");
+ } else {
+ OptionsElements.sponsorTimesOthersTimeSavedEndWord.innerText = chrome.i18n.getMessage("minLower");
+ }
+
+ OptionsElements.sponsorTimesOthersTimeSavedDisplay.innerText = getFormattedHours(minutesSaved);
+ OptionsElements.sponsorTimesOthersTimeSavedContainer.style.display = "unset";
+ }
+ }
+ });
+ }
+ }
+
+ //get the amount of times this user has skipped a sponsor
+ if (SB.config.skipCount != undefined) {
+ if (SB.config.skipCount != 1) {
+ OptionsElements.sponsorTimesSkipsDoneEndWord.innerText = chrome.i18n.getMessage("Sponsors");
+ } else {
+ OptionsElements.sponsorTimesSkipsDoneEndWord.innerText = chrome.i18n.getMessage("Sponsor");
+ }
+
+ OptionsElements.sponsorTimesSkipsDoneDisplay.innerText = SB.config.skipCount;
+ OptionsElements.sponsorTimesSkipsDoneContainer.style.display = "unset";
+ }
+
+ //get the amount of time this user has saved.
+ if (SB.config.minutesSaved != undefined) {
+ if (SB.config.minutesSaved != 1) {
+ OptionsElements.sponsorTimeSavedEndWord.innerText = chrome.i18n.getMessage("minsLower");
+ } else {
+ OptionsElements.sponsorTimeSavedEndWord.innerText = chrome.i18n.getMessage("minLower");
+ }
+
+ OptionsElements.sponsorTimeSavedDisplay.innerText = getFormattedHours(SB.config.minutesSaved);
+ OptionsElements.sponsorTimeSavedContainer.style.display = "unset";
+ }
+
+ messageHandler.query({
+ active: true,
+ currentWindow: true
+ }, onTabs);
+
+ function onTabs(tabs) {
+ messageHandler.sendMessage(tabs[0].id, {message: 'getVideoID'}, function(result) {
+ if (result != undefined && result.videoID) {
+ currentVideoID = result.videoID;
+ loadTabData(tabs);
+ } else if (result == undefined && chrome.runtime.lastError) {
+ //this isn't a YouTube video then, or at least the content script is not loaded
+ displayNoVideo();
+ }
+ });
+ }
+
+ function loadTabData(tabs) {
+ if (!currentVideoID) {
+ //this isn't a YouTube video then
+ displayNoVideo();
+ return;
+ }
+
+ //load video times for this video
+ let sponsorTimesStorage = SB.config.sponsorTimes.get(currentVideoID);
+ if (sponsorTimesStorage != undefined && sponsorTimesStorage.length > 0) {
+ if (sponsorTimesStorage[sponsorTimesStorage.length - 1] != undefined && sponsorTimesStorage[sponsorTimesStorage.length - 1].length < 2) {
+ startTimeChosen = true;
+ OptionsElements.sponsorStart.innerHTML = chrome.i18n.getMessage("sponsorEnd");
+ }
+
+ sponsorTimes = sponsorTimesStorage;
+
+ displaySponsorTimes();
+
+ //show submission section
+ OptionsElements.submissionSection.style.display = "unset";
+
+ showSubmitTimesIfNecessary();
+ }
+
+ //check if this video's sponsors are known
+ messageHandler.sendMessage(
+ tabs[0].id,
+ {message: 'isInfoFound'},
+ infoFound
+ );
+ }
+
+ function infoFound(request) {
+ if(chrome.runtime.lastError) {
+ //This page doesn't have the injected content script, or at least not yet
+ displayNoVideo();
+ return;
+ }
+
+ //if request is undefined, then the page currently being browsed is not YouTube
+ if (request != undefined) {
+ //remove loading text
+ OptionsElements.mainControls.style.display = "unset"
+ OptionsElements.loadingIndicator.style.display = "none";
+
+ if (request.found) {
+ OptionsElements.videoFound.innerHTML = chrome.i18n.getMessage("sponsorFound");
+
+ displayDownloadedSponsorTimes(request);
+ } else {
+ OptionsElements.videoFound.innerHTML = chrome.i18n.getMessage("sponsor404");
+ }
+ }
+
+ //see if whitelist button should be swapped
+ messageHandler.query({
+ active: true,
+ currentWindow: true
+ }, tabs => {
+ messageHandler.sendMessage(
+ tabs[0].id,
+ {message: 'isChannelWhitelisted'},
+ function(response) {
+ if (response.value) {
+ OptionsElements.whitelistChannel.style.display = "none";
+ OptionsElements.unwhitelistChannel.style.display = "unset";
+
+ OptionsElements.downloadedSponsorMessageTimes.innerText = chrome.i18n.getMessage("channelWhitelisted");
+ OptionsElements.downloadedSponsorMessageTimes.style.fontWeight = "bold";
+ }
+ });
+ }
+ );
+ }
+
+ function sendSponsorStartMessage() {
+ //the content script will get the message if a YouTube page is open
+ messageHandler.query({
+ active: true,
+ currentWindow: true
+ }, tabs => {
+ messageHandler.sendMessage(
+ tabs[0].id,
+ {from: 'popup', message: 'sponsorStart'},
+ startSponsorCallback
+ );
+ });
+ }
+
+ function startSponsorCallback(response) {
+ let sponsorTimesIndex = sponsorTimes.length - (startTimeChosen ? 1 : 0);
+
+ if (sponsorTimes[sponsorTimesIndex] == undefined) {
+ sponsorTimes[sponsorTimesIndex] = [];
+ }
+
+ sponsorTimes[sponsorTimesIndex][startTimeChosen ? 1 : 0] = response.time;
+
+ let localStartTimeChosen = startTimeChosen;
+ SB.config.sponsorTimes.set(currentVideoID, sponsorTimes);
+ //send a message to the client script
+ if (localStartTimeChosen) {
+ messageHandler.query({
+ active: true,
+ currentWindow: true
+ }, tabs => {
+ messageHandler.sendMessage(
+ tabs[0].id,
+ {message: "sponsorDataChanged"}
+ );
+ });
+ }
+
+ updateStartTimeChosen();
+
+ //display video times on screen
+ displaySponsorTimes();
+
+ //show submission section
+ OptionsElements.submissionSection.style.display = "unset";
+
+ showSubmitTimesIfNecessary();
+ }
+
+ //display the video times from the array
+ function displaySponsorTimes() {
+ //remove all children
+ while (OptionsElements.sponsorMessageTimes.firstChild) {
+ OptionsElements.sponsorMessageTimes.removeChild(OptionsElements.sponsorMessageTimes.firstChild);
+ }
+
+ //add sponsor times
+ OptionsElements.sponsorMessageTimes.appendChild(getSponsorTimesMessageDiv(sponsorTimes));
+ }
+
+ //display the video times from the array at the top, in a different section
+ function displayDownloadedSponsorTimes(request) {
+ if (request.sponsorTimes != undefined) {
+ //set it to the message
+ if (OptionsElements.downloadedSponsorMessageTimes.innerText != chrome.i18n.getMessage("channelWhitelisted")) {
+ OptionsElements.downloadedSponsorMessageTimes.innerText = getSponsorTimesMessage(request.sponsorTimes);
+ }
+
+ //add them as buttons to the issue reporting container
+ let container = document.getElementById("issueReporterTimeButtons");
+ for (let i = 0; i < request.sponsorTimes.length; i++) {
+ let sponsorTimeButton = document.createElement("button");
+ sponsorTimeButton.className = "warningButton popupElement";
+
+ let extraInfo = "";
+ if (request.hiddenSponsorTimes.includes(i)) {
+ //this one is hidden
+ extraInfo = " (hidden)";
+ }
+
+ sponsorTimeButton.innerText = getFormattedTime(request.sponsorTimes[i][0]) + " to " + getFormattedTime(request.sponsorTimes[i][1]) + extraInfo;
+
+ let votingButtons = document.createElement("div");
+
+ let UUID = request.UUIDs[i];
+
+ //thumbs up and down buttons
+ let voteButtonsContainer = document.createElement("div");
+ voteButtonsContainer.id = "sponsorTimesVoteButtonsContainer" + UUID;
+ voteButtonsContainer.setAttribute("align", "center");
+ voteButtonsContainer.style.display = "none"
+
+ let upvoteButton = document.createElement("img");
+ upvoteButton.id = "sponsorTimesUpvoteButtonsContainer" + UUID;
+ upvoteButton.className = "voteButton popupElement";
+ upvoteButton.src = chrome.extension.getURL("icons/upvote.png");
+ upvoteButton.addEventListener("click", () => vote(1, UUID));
+
+ let downvoteButton = document.createElement("img");
+ downvoteButton.id = "sponsorTimesDownvoteButtonsContainer" + UUID;
+ downvoteButton.className = "voteButton popupElement";
+ downvoteButton.src = chrome.extension.getURL("icons/downvote.png");
+ downvoteButton.addEventListener("click", () => vote(0, UUID));
+
+ //add thumbs up and down buttons to the container
+ voteButtonsContainer.appendChild(document.createElement("br"));
+ voteButtonsContainer.appendChild(document.createElement("br"));
+ voteButtonsContainer.appendChild(upvoteButton);
+ voteButtonsContainer.appendChild(downvoteButton);
+
+ //add click listener to open up vote panel
+ sponsorTimeButton.addEventListener("click", function() {
+ voteButtonsContainer.style.display = "unset";
+ });
+
+ container.appendChild(sponsorTimeButton);
+ container.appendChild(voteButtonsContainer);
+
+ //if it is not the last iteration
+ if (i != request.sponsorTimes.length - 1) {
+ container.appendChild(document.createElement("br"));
+ container.appendChild(document.createElement("br"));
+ }
+ }
+ }
+ }
+
+ //get the message that visually displays the video times
+ function getSponsorTimesMessage(sponsorTimes) {
+ let sponsorTimesMessage = "";
+
+ for (let i = 0; i < sponsorTimes.length; i++) {
+ for (let s = 0; s < sponsorTimes[i].length; s++) {
+ let timeMessage = getFormattedTime(sponsorTimes[i][s]);
+ //if this is an end time
+ if (s == 1) {
+ timeMessage = " to " + timeMessage;
+ } else if (i > 0) {
+ //add commas if necessary
+ timeMessage = ", " + timeMessage;
+ }
+
+ sponsorTimesMessage += timeMessage;
+ }
+ }
+
+ return sponsorTimesMessage;
+ }
+
+ //get the message that visually displays the video times
+ //this version is a div that contains each with delete buttons
+ function getSponsorTimesMessageDiv(sponsorTimes) {
+ // let sponsorTimesMessage = "";
+ let sponsorTimesContainer = document.createElement("div");
+ sponsorTimesContainer.id = "sponsorTimesContainer";
+
+ for (let i = 0; i < sponsorTimes.length; i++) {
+ let currentSponsorTimeContainer = document.createElement("div");
+ currentSponsorTimeContainer.id = "sponsorTimeContainer" + i;
+ currentSponsorTimeContainer.className = "sponsorTime popupElement";
+ let currentSponsorTimeMessage = "";
+
+ let deleteButton = document.createElement("span");
+ deleteButton.id = "sponsorTimeDeleteButton" + i;
+ deleteButton.innerText = "Delete";
+ deleteButton.className = "mediumLink popupElement";
+ let index = i;
+ deleteButton.addEventListener("click", () => deleteSponsorTime(index));
+
+ let previewButton = document.createElement("span");
+ previewButton.id = "sponsorTimePreviewButton" + i;
+ previewButton.innerText = "Preview";
+ previewButton.className = "mediumLink popupElement";
+ previewButton.addEventListener("click", () => previewSponsorTime(index));
+
+ let editButton = document.createElement("span");
+ editButton.id = "sponsorTimeEditButton" + i;
+ editButton.innerText = "Edit";
+ editButton.className = "mediumLink popupElement";
+ editButton.addEventListener("click", () => editSponsorTime(index));
+
+ for (let s = 0; s < sponsorTimes[i].length; s++) {
+ let timeMessage = getFormattedTime(sponsorTimes[i][s]);
+ //if this is an end time
+ if (s == 1) {
+ timeMessage = " to " + timeMessage;
+ } else if (i > 0) {
+ //add commas if necessary
+ timeMessage = timeMessage;
+ }
+
+ currentSponsorTimeMessage += timeMessage;
+ }
+
+ currentSponsorTimeContainer.innerText = currentSponsorTimeMessage;
+
+ sponsorTimesContainer.appendChild(currentSponsorTimeContainer);
+ sponsorTimesContainer.appendChild(deleteButton);
+
+ //only if it is a complete sponsor time
+ if (sponsorTimes[i].length > 1) {
+ sponsorTimesContainer.appendChild(previewButton);
+ sponsorTimesContainer.appendChild(editButton);
+
+ currentSponsorTimeContainer.addEventListener("click", () => editSponsorTime(index));
+ }
+ }
+
+ return sponsorTimesContainer;
+ }
+
+ function previewSponsorTime(index) {
+ let skipTime = sponsorTimes[index][0];
+
+ if (document.getElementById("startTimeMinutes" + index) != null) {
+ //edit is currently open, use that time
+
+ skipTime = getSponsorTimeEditTimes("startTime", index);
+
+ //save the edit
+ saveSponsorTimeEdit(index, false);
+ }
+
+ messageHandler.query({
+ active: true,
+ currentWindow: true
+ }, tabs => {
+ messageHandler.sendMessage(
+ tabs[0].id, {
+ message: "skipToTime",
+ time: skipTime - 2
+ }
+ );
+ });
+ }
+
+ function editSponsorTime(index) {
+ if (document.getElementById("startTimeMinutes" + index) != null) {
+ //already open
+ return;
+ }
+
+ //hide submit button
+ document.getElementById("submitTimesContainer").style.display = "none";
+
+ let sponsorTimeContainer = document.getElementById("sponsorTimeContainer" + index);
+
+ //the button to set the current time
+ let startTimeNowButton = document.createElement("span");
+ startTimeNowButton.id = "startTimeNowButton" + index;
+ startTimeNowButton.innerText = "(Now)";
+ startTimeNowButton.className = "tinyLink popupElement";
+ startTimeNowButton.addEventListener("click", () => setEditTimeToCurrentTime("startTime", index));
+
+ //get sponsor time minutes and seconds boxes
+ let startTimeMinutes = document.createElement("input");
+ startTimeMinutes.id = "startTimeMinutes" + index;
+ startTimeMinutes.className = "sponsorTime popupElement";
+ startTimeMinutes.type = "text";
+ startTimeMinutes.value = String(getTimeInMinutes(sponsorTimes[index][0]));
+ startTimeMinutes.style.width = "45px";
+
+ let startTimeSeconds = document.createElement("input");
+ startTimeSeconds.id = "startTimeSeconds" + index;
+ startTimeSeconds.className = "sponsorTime popupElement";
+ startTimeSeconds.type = "text";
+ startTimeSeconds.value = getTimeInFormattedSeconds(sponsorTimes[index][0]);
+ startTimeSeconds.style.width = "60px";
+
+ let endTimeMinutes = document.createElement("input");
+ endTimeMinutes.id = "endTimeMinutes" + index;
+ endTimeMinutes.className = "sponsorTime popupElement";
+ endTimeMinutes.type = "text";
+ endTimeMinutes.value = String(getTimeInMinutes(sponsorTimes[index][1]));
+ endTimeMinutes.style.width = "45px";
+
+ let endTimeSeconds = document.createElement("input");
+ endTimeSeconds.id = "endTimeSeconds" + index;
+ endTimeSeconds.className = "sponsorTime popupElement";
+ endTimeSeconds.type = "text";
+ endTimeSeconds.value = getTimeInFormattedSeconds(sponsorTimes[index][1]);
+ endTimeSeconds.style.width = "60px";
+
+ //the button to set the current time
+ let endTimeNowButton = document.createElement("span");
+ endTimeNowButton.id = "endTimeNowButton" + index;
+ endTimeNowButton.innerText = "(Now)";
+ endTimeNowButton.className = "tinyLink popupElement";
+ endTimeNowButton.addEventListener("click", () => setEditTimeToCurrentTime("endTime", index));
+
+ let colonText = document.createElement("span");
+ colonText.innerText = ":";
+
+ let toText = document.createElement("span");
+ toText.innerText = " to ";
+
+ //remove all children to replace
+ while (sponsorTimeContainer.firstChild) {
+ sponsorTimeContainer.removeChild(sponsorTimeContainer.firstChild);
+ }
+
+ sponsorTimeContainer.appendChild(startTimeNowButton);
+ sponsorTimeContainer.appendChild(startTimeMinutes);
+ sponsorTimeContainer.appendChild(colonText);
+ sponsorTimeContainer.appendChild(startTimeSeconds);
+ sponsorTimeContainer.appendChild(toText);
+ sponsorTimeContainer.appendChild(endTimeMinutes);
+ sponsorTimeContainer.appendChild(colonText);
+ sponsorTimeContainer.appendChild(endTimeSeconds);
+ sponsorTimeContainer.appendChild(endTimeNowButton);
+
+ //add save button and remove edit button
+ let saveButton = document.createElement("span");
+ saveButton.id = "sponsorTimeSaveButton" + index;
+ saveButton.innerText = "Save";
+ saveButton.className = "mediumLink popupElement";
+ saveButton.addEventListener("click", () => saveSponsorTimeEdit(index));
+
+ let editButton = document.getElementById("sponsorTimeEditButton" + index);
+ let sponsorTimesContainer = document.getElementById("sponsorTimesContainer");
+
+ sponsorTimesContainer.replaceChild(saveButton, editButton);
+ }
+
+ function setEditTimeToCurrentTime(idStartName, index) {
+ messageHandler.query({
+ active: true,
+ currentWindow: true
+ }, tabs => {
+ messageHandler.sendMessage(
+ tabs[0].id,
+ {message: "getCurrentTime"},
+ function (response) {
+ let minutes = document.getElementById(idStartName + "Minutes" + index);
+ let seconds = document.getElementById(idStartName + "Seconds" + index);
+
+ minutes.value = String(getTimeInMinutes(response.currentTime));
+ seconds.value = getTimeInFormattedSeconds(response.currentTime);
+ });
+ });
+ }
+
+ //id start name is whether it is the startTime or endTime
+ //gives back the time in seconds
+ function getSponsorTimeEditTimes(idStartName, index) {
+ let minutes = document.getElementById(idStartName + "Minutes" + index);
+ let seconds = document.getElementById(idStartName + "Seconds" + index);
+
+ return parseInt(minutes.value) * 60 + seconds.value;
+ }
+
+ function saveSponsorTimeEdit(index, closeEditMode = true) {
+ sponsorTimes[index][0] = getSponsorTimeEditTimes("startTime", index);
+ sponsorTimes[index][1] = getSponsorTimeEditTimes("endTime", index);
+
+ //save this
+ SB.config.sponsorTimes.set(currentVideoID, sponsorTimes);
+ messageHandler.query({
+ active: true,
+ currentWindow: true
+ }, tabs => {
+ messageHandler.sendMessage(
+ tabs[0].id,
+ {message: "sponsorDataChanged"}
+ );
+ });
+
+ if (closeEditMode) {
+ displaySponsorTimes();
+
+ showSubmitTimesIfNecessary();
+ }
+ }
+
+ //deletes the sponsor time submitted at an index
+ function deleteSponsorTime(index) {
+ //if it is not a complete sponsor time
+ if (sponsorTimes[index].length < 2) {
+ messageHandler.query({
+ active: true,
+ currentWindow: true
+ }, function(tabs) {
+ messageHandler.sendMessage(tabs[0].id, {
+ message: "changeStartSponsorButton",
+ showStartSponsor: true,
+ uploadButtonVisible: false
+ });
+ });
+
+ resetStartTimeChosen();
+ }
+
+ sponsorTimes.splice(index, 1);
+
+ //save this
+ SB.config.sponsorTimes.set(currentVideoID, sponsorTimes);
+ messageHandler.query({
+ active: true,
+ currentWindow: true
+ }, tabs => {
+ messageHandler.sendMessage(
+ tabs[0].id,
+ {message: "sponsorDataChanged"}
+ );
+ });
+
+ //update display
+ displaySponsorTimes();
+
+ //if they are all removed
+ if (sponsorTimes.length == 0) {
+ //update chrome tab
+ messageHandler.query({
+ active: true,
+ currentWindow: true
+ }, function(tabs) {
+ messageHandler.sendMessage(tabs[0].id, {
+ message: "changeStartSponsorButton",
+ showStartSponsor: true,
+ uploadButtonVisible: false
+ });
+ });
+
+ //hide submission section
+ document.getElementById("submissionSection").style.display = "none";
+ }
+ }
+
+ function clearTimes() {
+ //send new sponsor time state to tab
+ if (sponsorTimes.length > 0) {
+ messageHandler.query({
+ active: true,
+ currentWindow: true
+ }, function(tabs) {
+ messageHandler.sendMessage(tabs[0].id, {
+ message: "changeStartSponsorButton",
+ showStartSponsor: true,
+ uploadButtonVisible: false
+ });
+ });
+ }
+
+ //reset sponsorTimes
+ sponsorTimes = [];
+
+ SB.config.sponsorTimes.set(currentVideoID, sponsorTimes);
+ messageHandler.query({
+ active: true,
+ currentWindow: true
+ }, tabs => {
+ messageHandler.sendMessage(
+ tabs[0].id,
+ {message: "sponsorDataChanged"}
+ );
+ });
+
+ displaySponsorTimes();
+
+ //hide submission section
+ document.getElementById("submissionSection").style.display = "none";
+
+ resetStartTimeChosen();
+ }
+
+ function submitTimes() {
+ //make info message say loading
+ OptionsElements.submitTimesInfoMessage.innerText = chrome.i18n.getMessage("Loading");
+ OptionsElements.submitTimesInfoMessageContainer.style.display = "unset";
+
+ if (sponsorTimes.length > 0) {
+ chrome.runtime.sendMessage({
+ message: "submitTimes",
+ videoID: currentVideoID
+ }, function(response) {
+ if (response != undefined) {
+ if (response.statusCode == 200) {
+ //hide loading message
+ OptionsElements.submitTimesInfoMessageContainer.style.display = "none";
+
+ clearTimes();
+ } else {
+ document.getElementById("submitTimesInfoMessage").innerText = Utils.getErrorMessage(response.statusCode);
+ document.getElementById("submitTimesInfoMessageContainer").style.display = "unset";
+
+ OptionsElements.submitTimesInfoMessageContainer.style.display = "unset";
+ }
+ }
+ });
+ }
+ }
+
+ function showNoticeAgain() {
+ SB.config.dontShowNotice = false;
+
+ OptionsElements.showNoticeAgain.style.display = "none";
+ }
+
+ function updateStartTimeChosen() {
+ //update startTimeChosen letiable
+ if (!startTimeChosen) {
+ startTimeChosen = true;
+ OptionsElements.sponsorStart.innerHTML = chrome.i18n.getMessage("sponsorEnd");
+ } else {
+ resetStartTimeChosen();
+ }
+ }
+
+ //set it to false
+ function resetStartTimeChosen() {
+ startTimeChosen = false;
+ OptionsElements.sponsorStart.innerHTML = chrome.i18n.getMessage("sponsorStart");
+ }
+
+ //hides and shows the submit times button when needed
+ function showSubmitTimesIfNecessary() {
+ //check if an end time has been specified for the latest sponsor time
+ if (sponsorTimes.length > 0 && sponsorTimes[sponsorTimes.length - 1].length > 1) {
+ //show submit times button
+ document.getElementById("submitTimesContainer").style.display = "unset";
+ } else {
+ //hide submit times button
+ document.getElementById("submitTimesContainer").style.display = "none";
+ }
+ }
+
+ //make the options div visible
+ function openOptions() {
+ chrome.runtime.sendMessage({"message": "openConfig"});
+ }
+
+ //make the options username setting option visible
+ function setUsernameButton() {
+ //get username from the server
+ 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;
+
+ OptionsElements.submitUsername.style.display = "unset";
+ OptionsElements.usernameInput.style.display = "unset";
+
+ OptionsElements.setUsernameContainer.style.display = "none";
+ OptionsElements.setUsername.style.display = "unset";
+ OptionsElements
+ OptionsElements.setUsernameStatusContainer.style.display = "none";
+ } else if (xmlhttp.readyState == 4) {
+ OptionsElements.setUsername.style.display = "unset";
+ OptionsElements.submitUsername.style.display = "none";
+ OptionsElements.usernameInput.style.display = "none";
+
+ OptionsElements.setUsernameStatusContainer.style.display = "unset";
+ OptionsElements.setUsernameStatus.innerText = Utils.getErrorMessage(xmlhttp.status);
+ }
+ });
+ }
+
+ //submit the new username
+ function submitUsername() {
+ //add loading indicator
+ OptionsElements.setUsernameStatusContainer.style.display = "unset";
+ OptionsElements.setUsernameStatus.innerText = "Loading...";
+
+ //get the userID
+ 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";
+ OptionsElements.usernameInput.style.display = "none";
+
+ OptionsElements.setUsernameStatus.innerText = chrome.i18n.getMessage("success");
+ } else if (xmlhttp.readyState == 4) {
+ OptionsElements.setUsernameStatus.innerText = Utils.getErrorMessage(xmlhttp.status);
+ }
+ });
+
+
+ OptionsElements.setUsernameContainer.style.display = "none";
+ OptionsElements.setUsername.style.display = "unset";
+ }
+
+ //this is not a YouTube video page
+ function displayNoVideo() {
+ document.getElementById("loadingIndicator").innerText = chrome.i18n.getMessage("noVideoID");
+ }
+
+ function reportAnIssue() {
+ document.getElementById("issueReporterContainer").style.display = "unset";
+ OptionsElements.reportAnIssue.style.display = "none";
+ }
+
+ function addVoteMessage(message, UUID) {
+ let container = document.getElementById("sponsorTimesVoteButtonsContainer" + UUID);
+ //remove all children
+ while (container.firstChild) {
+ container.removeChild(container.firstChild);
+ }
+
+ let thanksForVotingText = document.createElement("h2");
+ thanksForVotingText.innerText = message;
+ //there are already breaks there
+ thanksForVotingText.style.marginBottom = "0px";
+
+ container.appendChild(thanksForVotingText);
+ }
+
+ function vote(type, UUID) {
+ //add loading info
+ addVoteMessage("Loading...", UUID)
+
+ //send the vote message to the tab
+ chrome.runtime.sendMessage({
+ message: "submitVote",
+ type: type,
+ UUID: UUID
+ }, function(response) {
+ if (response != undefined) {
+ //see if it was a success or failure
+ if (response.successType == 1 || (response.successType == -1 && response.statusCode == 429)) {
+ //success (treat rate limits as a success)
+ addVoteMessage(chrome.i18n.getMessage("voted"), UUID)
+ } else if (response.successType == 0) {
+ //failure: duplicate vote
+ addVoteMessage(chrome.i18n.getMessage("voteFail"), UUID)
+ } else if (response.successType == -1) {
+ addVoteMessage(Utils.getErrorMessage(response.statusCode), UUID)
+ }
+ }
+ });
+ }
+
+ function hideDiscordButton() {
+ SB.config.hideDiscordLink = true;
+ OptionsElements.discordButtonContainer.style.display = "none";
+ }
+
+ //converts time in seconds to minutes:seconds
+ function getFormattedTime(seconds) {
+ let minutes = Math.floor(seconds / 60);
+ let secondsDisplayNumber = Math.round(seconds - minutes * 60);
+ let secondsDisplay = String(secondsDisplayNumber);
+ if (secondsDisplayNumber < 10) {
+ //add a zero
+ secondsDisplay = "0" + secondsDisplay;
+ }
+
+ let formatted = minutes+ ":" + secondsDisplay;
+
+ return formatted;
+ }
+
+ function whitelistChannel() {
+ //get the channel url
+ messageHandler.query({
+ active: true,
+ currentWindow: true
+ }, tabs => {
+ messageHandler.sendMessage(
+ tabs[0].id,
+ {message: 'getChannelURL'},
+ function(response) {
+ //get whitelisted channels
+ let whitelistedChannels = SB.config.whitelistedChannels;
+ if (whitelistedChannels == undefined) {
+ whitelistedChannels = [];
+ }
+
+ //add on this channel
+ whitelistedChannels.push(response.channelURL);
+
+ //change button
+ OptionsElements.whitelistChannel.style.display = "none";
+ OptionsElements.unwhitelistChannel.style.display = "unset";
+
+ OptionsElements.downloadedSponsorMessageTimes.innerText = chrome.i18n.getMessage("channelWhitelisted");
+ OptionsElements.downloadedSponsorMessageTimes.style.fontWeight = "bold";
+
+ //save this
+ OptionsElements.config.whitelistedChannels = whitelistedChannels;
+
+ //send a message to the client
+ messageHandler.query({
+ active: true,
+ currentWindow: true
+ }, tabs => {
+ messageHandler.sendMessage(
+ tabs[0].id, {
+ message: 'whitelistChange',
+ value: true
+ });
+ }
+ );
+ }
+ );
+ });
+ }
+
+ function unwhitelistChannel() {
+ //get the channel url
+ messageHandler.query({
+ active: true,
+ currentWindow: true
+ }, tabs => {
+ messageHandler.sendMessage(
+ tabs[0].id,
+ {message: 'getChannelURL'},
+ function(response) {
+ //get whitelisted channels
+ let whitelistedChannels = SB.config.whitelistedChannels;
+ if (whitelistedChannels == undefined) {
+ whitelistedChannels = [];
+ }
+
+ //remove this channel
+ let index = whitelistedChannels.indexOf(response.channelURL);
+ whitelistedChannels.splice(index, 1);
+
+ //change button
+ OptionsElements.whitelistChannel.style.display = "unset";
+ OptionsElements.unwhitelistChannel.style.display = "none";
+
+ OptionsElements.downloadedSponsorMessageTimes.innerText = "";
+ OptionsElements.downloadedSponsorMessageTimes.style.fontWeight = "unset";
+
+ //save this
+ OptionsElements.config.whitelistedChannels = whitelistedChannels;
+
+ //send a message to the client
+ messageHandler.query({
+ active: true,
+ currentWindow: true
+ }, tabs => {
+ messageHandler.sendMessage(
+ tabs[0].id, {
+ message: 'whitelistChange',
+ value: false
+ });
+ }
+ );
+ }
+ );
+ });
+ }
+
+ /**
+ * Should skipping be disabled (visuals stay)
+ */
+ function toggleSkipping(disabled) {
+ OptionsElements.config.disableSkipping = disabled;
+
+ let hiddenButton = OptionsElements.disableSkipping;
+ let shownButton = OptionsElements.enableSkipping;
+
+ if (!disabled) {
+ hiddenButton = OptionsElements.enableSkipping;
+ shownButton = OptionsElements.disableSkipping;
+ }
+
+ shownButton.style.display = "unset";
+ hiddenButton.style.display = "none";
+ }
+
+ //converts time in seconds to minutes
+ function getTimeInMinutes(seconds) {
+ let minutes = Math.floor(seconds / 60);
+
+ return minutes;
+ }
+
+ //converts time in seconds to seconds past the last minute
+ function getTimeInFormattedSeconds(seconds) {
+ let minutes = seconds % 60;
+ let secondsFormatted = minutes.toFixed(3);
+
+ if (minutes < 10) {
+ secondsFormatted = "0" + secondsFormatted;
+ }
+
+ return secondsFormatted;
+ }
+
+ function sendRequestToServer(type, address, callback) {
+ let xmlhttp = new XMLHttpRequest();
+
+ xmlhttp.open(type, CompileConfig.serverAddress + address, true);
+
+ if (callback != undefined) {
+ xmlhttp.onreadystatechange = function () {
+ callback(xmlhttp, false);
+ };
+
+ xmlhttp.onerror = function(ev) {
+ callback(xmlhttp, true);
+ };
+ }
+
+ //submit this request
+ xmlhttp.send();
+ }
+
+ /**
+ * Converts time in hours to 5h 25.1
+ * If less than 1 hour, just returns minutes
+ *
+ * @param {float} seconds
+ * @returns {string}
+ */
+ function getFormattedHours(minues) {
+ let hours = Math.floor(minues / 60);
+ return (hours > 0 ? hours + "h " : "") + (minues % 60).toFixed(1);
+ }
+
+//end of function
+}
+
+if (chrome.tabs != undefined) {
+ //add the width restriction (because Firefox)
+ let link = document.getElementById("sponsorBlockStyleSheet");
+ ( link.sheet).insertRule('.popupBody { width: 325 }', 0);
+
+ //this means it is actually opened in the popup
+ runThePopup();
+}
diff --git a/tsconfig.json b/tsconfig.json
index 21b02419..cc0b77e6 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -4,9 +4,9 @@
"target": "es6",
"noImplicitAny": false,
"sourceMap": false,
- "rootDir": "src",
"outDir": "dist/js",
"noEmitOnError": true,
- "typeRoots": [ "node_modules/@types" ]
+ "typeRoots": [ "node_modules/@types" ],
+ "resolveJsonModule": true
}
}
\ No newline at end of file
diff --git a/webpack/webpack.common.js b/webpack/webpack.common.js
index b70636ad..27d7fc1a 100644
--- a/webpack/webpack.common.js
+++ b/webpack/webpack.common.js
@@ -7,7 +7,7 @@ module.exports = {
entry: {
popup: path.join(__dirname, srcDir + 'popup.ts'),
background: path.join(__dirname, srcDir + 'background.ts'),
- content_script: path.join(__dirname, srcDir + 'content_script.ts')
+ content_script: path.join(__dirname, srcDir + 'content.ts')
},
output: {
path: path.join(__dirname, '../dist/js'),