diff --git a/.eslintrc.js b/.eslintrc.js index 64b64a12c9..278f30f42e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -19,9 +19,15 @@ module.exports = { }, plugins: ["react", "@typescript-eslint"], rules: { + // silence some eslint:recommended rules // TODO: Remove warn rules when not needed anymore "no-self-assign": "off", "@typescript-eslint/no-empty-interface": "off", + + // .editorconfig: + "linebreak-style": ["error", "unix"], + "eol-last": "error", + "indent": ["error", 4], }, settings: { react: { diff --git a/src/background.ts b/src/background.ts index e9643667e4..4b905cd3dd 100644 --- a/src/background.ts +++ b/src/background.ts @@ -24,51 +24,51 @@ if (utils.isFirefox()) { } chrome.tabs.onUpdated.addListener(function(tabId) { - chrome.tabs.sendMessage(tabId, { + chrome.tabs.sendMessage(tabId, { message: 'update', - }, () => void chrome.runtime.lastError ); // Suppress error on Firefox + }, () => void chrome.runtime.lastError ); // Suppress error on Firefox }); chrome.runtime.onMessage.addListener(function (request, sender, callback) { - switch(request.message) { - case "openConfig": - chrome.runtime.openOptionsPage(); - return; - case "openHelp": - chrome.tabs.create({url: chrome.runtime.getURL('help/index_en.html')}); - return; - case "sendRequest": - sendRequestToCustomServer(request.type, request.url, request.data).then(async (response) => { - callback({ - responseText: await response.text(), - status: response.status, - ok: response.ok - }); + switch(request.message) { + case "openConfig": + chrome.runtime.openOptionsPage(); + return; + case "openHelp": + chrome.tabs.create({url: chrome.runtime.getURL('help/index_en.html')}); + return; + case "sendRequest": + sendRequestToCustomServer(request.type, request.url, request.data).then(async (response) => { + callback({ + responseText: await response.text(), + status: response.status, + ok: response.ok }); + }); - return true; - case "submitVote": - submitVote(request.type, request.UUID, request.category).then(callback); + return true; + case "submitVote": + submitVote(request.type, request.UUID, request.category).then(callback); - //this allows the callback to be called later - return true; - case "alertPrevious": - if (Config.config.unsubmittedWarning) { - chrome.notifications.create("stillThere" + Math.random(), { - type: "basic", - title: chrome.i18n.getMessage("wantToSubmit") + " " + request.previousVideoID + "?", - message: chrome.i18n.getMessage("leftTimes"), - iconUrl: "./icons/LogoSponsorBlocker256px.png" - }); - } - break; - case "registerContentScript": - registerFirefoxContentScript(request); - return false; - case "unregisterContentScript": - unregisterFirefoxContentScript(request.id) - return false; - } + //this allows the callback to be called later + return true; + case "alertPrevious": + if (Config.config.unsubmittedWarning) { + chrome.notifications.create("stillThere" + Math.random(), { + type: "basic", + title: chrome.i18n.getMessage("wantToSubmit") + " " + request.previousVideoID + "?", + message: chrome.i18n.getMessage("leftTimes"), + iconUrl: "./icons/LogoSponsorBlocker256px.png" + }); + } + break; + case "registerContentScript": + registerFirefoxContentScript(request); + return false; + case "unregisterContentScript": + unregisterFirefoxContentScript(request.id) + return false; + } }); //add help page on install @@ -187,4 +187,4 @@ async function sendRequestToCustomServer(type: string, url: string, data = {}) { }); return response; -} \ No newline at end of file +} diff --git a/src/components/CategoryChooserComponent.tsx b/src/components/CategoryChooserComponent.tsx index bb86e10fa5..f431428ba6 100644 --- a/src/components/CategoryChooserComponent.tsx +++ b/src/components/CategoryChooserComponent.tsx @@ -29,7 +29,7 @@ class CategoryChooserComponent extends React.Component {/* Headers */} + className="categoryTableElement categoryTableHeader"> {chrome.i18n.getMessage("category")} @@ -68,4 +68,4 @@ class CategoryChooserComponent extends React.Component - {chrome.i18n.getMessage("category_" + this.props.category)} + {chrome.i18n.getMessage("category_" + this.props.category)} @@ -61,7 +61,7 @@ class CategorySkipOptionsComponent extends React.Component - {this.getCategorySkipOptions()} + {this.getCategorySkipOptions()} @@ -85,10 +85,10 @@ class CategorySkipOptionsComponent extends React.Component - - {chrome.i18n.getMessage("category_" + this.props.category + "_description")} - + + {chrome.i18n.getMessage("category_" + this.props.category + "_description")} + @@ -101,20 +101,20 @@ class CategorySkipOptionsComponent extends React.Component - {this.props.text} + {this.props.text}

); } } -export default NoticeTextSelectionComponent; \ No newline at end of file +export default NoticeTextSelectionComponent; diff --git a/src/components/SkipNoticeComponent.tsx b/src/components/SkipNoticeComponent.tsx index aa75a5872c..b43a365892 100644 --- a/src/components/SkipNoticeComponent.tsx +++ b/src/components/SkipNoticeComponent.tsx @@ -190,8 +190,8 @@ class SkipNoticeComponent extends React.Component + className="sponsorTimesInfoMessage sponsorTimesVoteButtonMessage" + style={{marginRight: "10px"}}> {this.state.thanksForVotingText} } @@ -226,13 +226,13 @@ class SkipNoticeComponent extends React.Component this.prepAction(SkipNoticeAction.Downvote)}> + onClick={() => this.prepAction(SkipNoticeAction.Downvote)}> {chrome.i18n.getMessage("downvoteDescription")} {/* Category vote */} @@ -247,10 +247,10 @@ class SkipNoticeComponent extends React.Component {/* Category Selector */} @@ -258,7 +258,7 @@ class SkipNoticeComponent extends React.Component this.prepAction(SkipNoticeAction.CategoryVote)}> + onClick={() => this.prepAction(SkipNoticeAction.CategoryVote)}> {chrome.i18n.getMessage("submit")} @@ -287,8 +287,8 @@ class SkipNoticeComponent extends React.Component this.performAction(i)} - key={"submission" + i + this.segments[i].category + this.idSuffix}> + onClick={() => this.performAction(i)} + key={"submission" + i + this.segments[i].category + this.idSuffix}> {(i + 1) + ". " + chrome.i18n.getMessage("category_" + this.segments[i].category)} ); @@ -339,20 +339,20 @@ class SkipNoticeComponent extends React.Component + key={category.name}> {chrome.i18n.getMessage("category_" + category.name)} ); @@ -408,7 +408,7 @@ class SkipNoticeComponent extends React.Component + key={"moreCategories"}> {chrome.i18n.getMessage("moreCategories")} ); diff --git a/src/components/SponsorTimeEditComponent.tsx b/src/components/SponsorTimeEditComponent.tsx index e00813ca6c..7e49d4d724 100644 --- a/src/components/SponsorTimeEditComponent.tsx +++ b/src/components/SponsorTimeEditComponent.tsx @@ -92,57 +92,57 @@ class SponsorTimeEditComponent extends React.Component - this.setTimeToNow(0)}> - {chrome.i18n.getMessage("bracketNow")} - - - { - const sponsorTimeEdits = this.state.sponsorTimeEdits; - sponsorTimeEdits[0] = e.target.value; - - this.setState({sponsorTimeEdits}); - - this.saveEditTimes(); - }}> - - - - {" " + chrome.i18n.getMessage("to") + " "} - - - { - const sponsorTimeEdits = this.state.sponsorTimeEdits; - sponsorTimeEdits[1] = e.target.value; - - this.setState({sponsorTimeEdits}); - - this.saveEditTimes(); - }}> - - - this.setTimeToNow(1)}> - {chrome.i18n.getMessage("bracketNow")} - - - this.setTimeToEnd()}> - {chrome.i18n.getMessage("bracketEnd")} - + this.setTimeToNow(0)}> + {chrome.i18n.getMessage("bracketNow")} + + + { + const sponsorTimeEdits = this.state.sponsorTimeEdits; + sponsorTimeEdits[0] = e.target.value; + + this.setState({sponsorTimeEdits}); + + this.saveEditTimes(); + }}> + + + + {" " + chrome.i18n.getMessage("to") + " "} + + + { + const sponsorTimeEdits = this.state.sponsorTimeEdits; + sponsorTimeEdits[1] = e.target.value; + + this.setState({sponsorTimeEdits}); + + this.saveEditTimes(); + }}> + + + this.setTimeToNow(1)}> + {chrome.i18n.getMessage("bracketNow")} + + + this.setTimeToEnd()}> + {chrome.i18n.getMessage("bracketEnd")} + ); } else { @@ -150,7 +150,7 @@ class SponsorTimeEditComponent extends React.Component - {utils.getFormattedTime(segment[0], true) + + {utils.getFormattedTime(segment[0], true) + ((!isNaN(segment[1])) ? " " + chrome.i18n.getMessage("to") + " " + utils.getFormattedTime(segment[1], true) : "")} ); @@ -220,7 +220,7 @@ class SponsorTimeEditComponent extends React.Component + key={DEFAULT_CATEGORY}> {chrome.i18n.getMessage(DEFAULT_CATEGORY)} )]; @@ -228,7 +228,7 @@ class SponsorTimeEditComponent extends React.Component + key={category}> {chrome.i18n.getMessage("category_" + category)} ); @@ -245,7 +245,7 @@ class SponsorTimeEditComponent extends React.Component void): void { //messages from popup script switch(request.message){ - case "update": - videoIDChange(getYouTubeVideoID(document.URL)); - break; - case "sponsorStart": - sponsorMessageStarted(sendResponse); - - break; - case "sponsorDataChanged": - updateSponsorTimesSubmitting(); - - break; - case "isInfoFound": - //send the sponsor times along with if it's found - sendResponse({ - found: sponsorDataFound, - sponsorTimes: sponsorTimes - }); + case "update": + videoIDChange(getYouTubeVideoID(document.URL)); + break; + case "sponsorStart": + sponsorMessageStarted(sendResponse); + + break; + case "sponsorDataChanged": + updateSponsorTimesSubmitting(); + + break; + case "isInfoFound": + //send the sponsor times along with if it's found + sendResponse({ + found: sponsorDataFound, + sponsorTimes: sponsorTimes + }); - if (popupInitialised && document.getElementById("sponsorBlockPopupContainer") != null) { - //the popup should be closed now that another is opening - closeInfoMenu(); - } + if (popupInitialised && document.getElementById("sponsorBlockPopupContainer") != null) { + //the popup should be closed now that another is opening + closeInfoMenu(); + } - popupInitialised = true; - break; - case "getVideoID": - sendResponse({ - videoID: sponsorVideoID - }); + popupInitialised = true; + break; + case "getVideoID": + sendResponse({ + videoID: sponsorVideoID + }); - break; - case "getChannelID": - sendResponse({ - channelID: channelID - }); + break; + case "getChannelID": + sendResponse({ + channelID: channelID + }); - break; - case "isChannelWhitelisted": - sendResponse({ - value: channelWhitelisted - }); + break; + case "isChannelWhitelisted": + sendResponse({ + value: channelWhitelisted + }); - break; - case "whitelistChange": - channelWhitelisted = request.value; - sponsorsLookup(sponsorVideoID); + break; + case "whitelistChange": + channelWhitelisted = request.value; + sponsorsLookup(sponsorVideoID); - break; - case "changeStartSponsorButton": - changeStartSponsorButton(request.showStartSponsor, request.uploadButtonVisible); + break; + case "changeStartSponsorButton": + changeStartSponsorButton(request.showStartSponsor, request.uploadButtonVisible); - break; - case "submitTimes": - submitSponsorTimes(); - break; + break; + case "submitTimes": + submitSponsorTimes(); + break; } } @@ -185,11 +185,11 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo function contentConfigUpdateListener(changes: StorageChangesObject) { for (const key in changes) { switch(key) { - case "hideVideoPlayerControls": - case "hideInfoButtonPlayerControls": - case "hideDeleteButtonPlayerControls": - updateVisibilityOfPlayerControlsButton() - break; + case "hideVideoPlayerControls": + case "hideInfoButtonPlayerControls": + case "hideDeleteButtonPlayerControls": + updateVisibilityOfPlayerControlsButton() + break; } } } @@ -262,7 +262,7 @@ async function videoIDChange(id) { resetValues(); - //id is not valid + //id is not valid if (!id) return; // Wait for options to be ready @@ -932,8 +932,8 @@ function getLatestEndTimeIndex(sponsorTimes: SponsorTime[], index: number, hideH if (currentSegment[0] <= latestEndTime && currentSegment[1] > latestEndTime && (!hideHiddenSponsors || sponsorTimes[i].hidden === SponsorHideType.Visible) && utils.getCategorySelection(sponsorTimes[i].category).option === CategorySkipOption.AutoSkip) { - // Overlapping segment - latestEndTimeIndex = i; + // Overlapping segment + latestEndTimeIndex = i; } } @@ -1617,34 +1617,34 @@ function updateAdFlag(): void { function showTimeWithoutSkips(allSponsorTimes): void { if (onMobileYouTube || onInvidious) return; - let skipDuration = 0; + let skipDuration = 0; - // Calculate skipDuration based from the segments in the preview bar - for (let i = 0; i < allSponsorTimes.length; i++) { + // Calculate skipDuration based from the segments in the preview bar + for (let i = 0; i < allSponsorTimes.length; i++) { // If an end time exists if (allSponsorTimes[i].segment[1]) { skipDuration += allSponsorTimes[i].segment[1] - allSponsorTimes[i].segment[0]; } - } + } - // YouTube player time display - const display = document.getElementsByClassName("ytp-time-display notranslate")[0]; - if (!display) return; + // YouTube player time display + const display = document.getElementsByClassName("ytp-time-display notranslate")[0]; + if (!display) return; const formatedTime = utils.getFormattedTime(video.duration - skipDuration); - const durationID = "sponsorBlockDurationAfterSkips"; + const durationID = "sponsorBlockDurationAfterSkips"; let duration = document.getElementById(durationID); - // Create span if needed - if(duration === null) { - duration = document.createElement('span'); + // Create span if needed + if(duration === null) { + duration = document.createElement('span'); duration.id = durationID; duration.classList.add("ytp-time-duration"); - display.appendChild(duration); - } + display.appendChild(duration); + } duration.innerText = (skipDuration <= 0 || isNaN(skipDuration) || formatedTime.includes("NaN")) ? "" : " ("+formatedTime+")"; } diff --git a/src/js-components/previewBar.ts b/src/js-components/previewBar.ts index a1f4e2c3e4..3ada0d2a7b 100644 --- a/src/js-components/previewBar.ts +++ b/src/js-components/previewBar.ts @@ -10,178 +10,178 @@ import Utils from "../utils"; const utils = new Utils(); class PreviewBar { - container: HTMLUListElement; - parent: HTMLElement; - onMobileYouTube: boolean; - onInvidious: boolean; - - timestamps: number[][]; - types: string[]; - - constructor(parent: HTMLElement, onMobileYouTube: boolean, onInvidious: boolean) { - this.container = document.createElement('ul'); - this.container.id = 'previewbar'; - this.parent = parent; - - this.onMobileYouTube = onMobileYouTube; - this.onInvidious = onInvidious; - - this.updatePosition(parent); - - this.setupHoverText(); - } - - setupHoverText(): void { - if (this.onMobileYouTube || this.onInvidious) return; - - const seekBar = document.querySelector(".ytp-progress-bar-container"); - - // Create label placeholder - const tooltipTextWrapper = document.querySelector(".ytp-tooltip-text-wrapper"); - const titleTooltip = document.querySelector(".ytp-tooltip-title"); - const categoryTooltip = document.createElement("div"); - categoryTooltip.className = "sbHidden ytp-tooltip-title"; - categoryTooltip.id = "sponsor-block-category-tooltip" - - tooltipTextWrapper.insertBefore(categoryTooltip, titleTooltip.nextSibling); - - let mouseOnSeekBar = false; - - seekBar.addEventListener("mouseenter", () => { - mouseOnSeekBar = true; - }); - - seekBar.addEventListener("mouseleave", () => { - mouseOnSeekBar = false; - categoryTooltip.classList.add("sbHidden"); - }); - - const observer = new MutationObserver((mutations) => { - if (!mouseOnSeekBar) return; - - // See if mutation observed is only this ID (if so, ignore) - if (mutations.length == 1 && (mutations[0].target as HTMLElement).id === "sponsor-block-category-tooltip") { - return; - } - - const tooltips = document.querySelectorAll(".ytp-tooltip-text"); - for (const tooltip of tooltips) { - const splitData = tooltip.textContent.split(":"); - if (splitData.length === 2 && !isNaN(parseInt(splitData[0])) && !isNaN(parseInt(splitData[1]))) { - // Add label - const timeInSeconds = parseInt(splitData[0]) * 60 + parseInt(splitData[1]); - - // Find category at that location - let category = null; - for (let i = 0; i < this.timestamps?.length; i++) { - if (this.timestamps[i][0] < timeInSeconds && this.timestamps[i][1] > timeInSeconds){ - category = this.types[i]; - } - } - - if (category === null && !categoryTooltip.classList.contains("sbHidden")) { - categoryTooltip.classList.add("sbHidden"); - tooltipTextWrapper.classList.remove("sbTooltipTwoTitleThumbnailOffset"); - tooltipTextWrapper.classList.remove("sbTooltipOneTitleThumbnailOffset"); - } else if (category !== null) { - categoryTooltip.classList.remove("sbHidden"); - categoryTooltip.textContent = utils.shortCategoryName(category) - || (chrome.i18n.getMessage("preview") + " " + utils.shortCategoryName(category.split("preview-")[1])); - - // There is a title now - tooltip.classList.remove("ytp-tooltip-text-no-title"); - - // Add the correct offset for the number of titles there are - if (titleTooltip.textContent !== "") { - if (!tooltipTextWrapper.classList.contains("sbTooltipTwoTitleThumbnailOffset")) { - tooltipTextWrapper.classList.add("sbTooltipTwoTitleThumbnailOffset"); - } - } else if (!tooltipTextWrapper.classList.contains("sbTooltipOneTitleThumbnailOffset")) { - tooltipTextWrapper.classList.add("sbTooltipOneTitleThumbnailOffset"); - } - } - - break; - } - } - }); - - observer.observe(tooltipTextWrapper, { - childList: true, - subtree: true - }); - } - - updatePosition(parent: HTMLElement): void { - //below the seek bar - // this.parent.insertAdjacentElement("afterEnd", this.container); - - this.parent = parent; - - if (this.onMobileYouTube) { - parent.style.backgroundColor = "rgba(255, 255, 255, 0.3)"; - parent.style.opacity = "1"; - - this.container.style.transform = "none"; - } - - //on the seek bar - this.parent.insertAdjacentElement("afterbegin", this.container); - } - - updateColor(segment: string, color: string, opacity: string): void { - const bars = > document.querySelectorAll('[data-vs-segment-type=' + segment + ']'); - for (const bar of bars) { - bar.style.backgroundColor = color; - bar.style.opacity = opacity; - } - } - - set(timestamps: number[][], types: string[], duration: number): void { - while (this.container.firstChild) { - this.container.removeChild(this.container.firstChild); - } - - if (!timestamps || !types) { - return; - } - - this.timestamps = timestamps; - this.types = types; - - // to avoid rounding error resulting in width more than 100% - duration = Math.floor(duration * 100) / 100; - let width; - for (let i = 0; i < timestamps.length; i++) { - if (types[i] == null) continue; - - width = (timestamps[i][1] - timestamps[i][0]) / duration * 100; - width = Math.floor(width * 100) / 100; - - const bar = this.createBar(); - bar.setAttribute('data-vs-segment-type', types[i]); - - bar.style.backgroundColor = Config.config.barTypes[types[i]].color; - if (!this.onMobileYouTube) bar.style.opacity = Config.config.barTypes[types[i]].opacity; - bar.style.width = width + '%'; - bar.style.left = (timestamps[i][0] / duration * 100) + "%"; - bar.style.position = "absolute" - - this.container.insertAdjacentElement("beforeend", bar); - } - } - - createBar(): HTMLLIElement { - const bar = document.createElement('li'); - bar.classList.add('previewbar'); - bar.innerHTML = ' '; - return bar; - } - - remove(): void { - this.container.remove(); - this.container = undefined; - } + container: HTMLUListElement; + parent: HTMLElement; + onMobileYouTube: boolean; + onInvidious: boolean; + + timestamps: number[][]; + types: string[]; + + constructor(parent: HTMLElement, onMobileYouTube: boolean, onInvidious: boolean) { + this.container = document.createElement('ul'); + this.container.id = 'previewbar'; + this.parent = parent; + + this.onMobileYouTube = onMobileYouTube; + this.onInvidious = onInvidious; + + this.updatePosition(parent); + + this.setupHoverText(); + } + + setupHoverText(): void { + if (this.onMobileYouTube || this.onInvidious) return; + + const seekBar = document.querySelector(".ytp-progress-bar-container"); + + // Create label placeholder + const tooltipTextWrapper = document.querySelector(".ytp-tooltip-text-wrapper"); + const titleTooltip = document.querySelector(".ytp-tooltip-title"); + const categoryTooltip = document.createElement("div"); + categoryTooltip.className = "sbHidden ytp-tooltip-title"; + categoryTooltip.id = "sponsor-block-category-tooltip" + + tooltipTextWrapper.insertBefore(categoryTooltip, titleTooltip.nextSibling); + + let mouseOnSeekBar = false; + + seekBar.addEventListener("mouseenter", () => { + mouseOnSeekBar = true; + }); + + seekBar.addEventListener("mouseleave", () => { + mouseOnSeekBar = false; + categoryTooltip.classList.add("sbHidden"); + }); + + const observer = new MutationObserver((mutations) => { + if (!mouseOnSeekBar) return; + + // See if mutation observed is only this ID (if so, ignore) + if (mutations.length == 1 && (mutations[0].target as HTMLElement).id === "sponsor-block-category-tooltip") { + return; + } + + const tooltips = document.querySelectorAll(".ytp-tooltip-text"); + for (const tooltip of tooltips) { + const splitData = tooltip.textContent.split(":"); + if (splitData.length === 2 && !isNaN(parseInt(splitData[0])) && !isNaN(parseInt(splitData[1]))) { + // Add label + const timeInSeconds = parseInt(splitData[0]) * 60 + parseInt(splitData[1]); + + // Find category at that location + let category = null; + for (let i = 0; i < this.timestamps?.length; i++) { + if (this.timestamps[i][0] < timeInSeconds && this.timestamps[i][1] > timeInSeconds){ + category = this.types[i]; + } + } + + if (category === null && !categoryTooltip.classList.contains("sbHidden")) { + categoryTooltip.classList.add("sbHidden"); + tooltipTextWrapper.classList.remove("sbTooltipTwoTitleThumbnailOffset"); + tooltipTextWrapper.classList.remove("sbTooltipOneTitleThumbnailOffset"); + } else if (category !== null) { + categoryTooltip.classList.remove("sbHidden"); + categoryTooltip.textContent = utils.shortCategoryName(category) + || (chrome.i18n.getMessage("preview") + " " + utils.shortCategoryName(category.split("preview-")[1])); + + // There is a title now + tooltip.classList.remove("ytp-tooltip-text-no-title"); + + // Add the correct offset for the number of titles there are + if (titleTooltip.textContent !== "") { + if (!tooltipTextWrapper.classList.contains("sbTooltipTwoTitleThumbnailOffset")) { + tooltipTextWrapper.classList.add("sbTooltipTwoTitleThumbnailOffset"); + } + } else if (!tooltipTextWrapper.classList.contains("sbTooltipOneTitleThumbnailOffset")) { + tooltipTextWrapper.classList.add("sbTooltipOneTitleThumbnailOffset"); + } + } + + break; + } + } + }); + + observer.observe(tooltipTextWrapper, { + childList: true, + subtree: true + }); + } + + updatePosition(parent: HTMLElement): void { + //below the seek bar + // this.parent.insertAdjacentElement("afterEnd", this.container); + + this.parent = parent; + + if (this.onMobileYouTube) { + parent.style.backgroundColor = "rgba(255, 255, 255, 0.3)"; + parent.style.opacity = "1"; + + this.container.style.transform = "none"; + } + + //on the seek bar + this.parent.insertAdjacentElement("afterbegin", this.container); + } + + updateColor(segment: string, color: string, opacity: string): void { + const bars = > document.querySelectorAll('[data-vs-segment-type=' + segment + ']'); + for (const bar of bars) { + bar.style.backgroundColor = color; + bar.style.opacity = opacity; + } + } + + set(timestamps: number[][], types: string[], duration: number): void { + while (this.container.firstChild) { + this.container.removeChild(this.container.firstChild); + } + + if (!timestamps || !types) { + return; + } + + this.timestamps = timestamps; + this.types = types; + + // to avoid rounding error resulting in width more than 100% + duration = Math.floor(duration * 100) / 100; + let width; + for (let i = 0; i < timestamps.length; i++) { + if (types[i] == null) continue; + + width = (timestamps[i][1] - timestamps[i][0]) / duration * 100; + width = Math.floor(width * 100) / 100; + + const bar = this.createBar(); + bar.setAttribute('data-vs-segment-type', types[i]); + + bar.style.backgroundColor = Config.config.barTypes[types[i]].color; + if (!this.onMobileYouTube) bar.style.opacity = Config.config.barTypes[types[i]].opacity; + bar.style.width = width + '%'; + bar.style.left = (timestamps[i][0] / duration * 100) + "%"; + bar.style.position = "absolute" + + this.container.insertAdjacentElement("beforeend", bar); + } + } + + createBar(): HTMLLIElement { + const bar = document.createElement('li'); + bar.classList.add('previewbar'); + bar.innerHTML = ' '; + return bar; + } + + remove(): void { + this.container.remove(); + this.container = undefined; + } } -export default PreviewBar; \ No newline at end of file +export default PreviewBar; diff --git a/src/options.ts b/src/options.ts index 186e442aed..39b0aa317f 100644 --- a/src/options.ts +++ b/src/options.ts @@ -32,163 +32,163 @@ async function init() { for (let i = 0; i < optionsElements.length; i++) { switch (optionsElements[i].getAttribute("option-type")) { - case "toggle": { - const option = optionsElements[i].getAttribute("sync-option"); - const optionResult = Config.config[option]; + case "toggle": { + const option = optionsElements[i].getAttribute("sync-option"); + const optionResult = Config.config[option]; - const checkbox = optionsElements[i].querySelector("input"); - const reverse = optionsElements[i].getAttribute("toggle-type") === "reverse"; + const checkbox = optionsElements[i].querySelector("input"); + const reverse = optionsElements[i].getAttribute("toggle-type") === "reverse"; - const confirmMessage = optionsElements[i].getAttribute("confirm-message"); + const confirmMessage = optionsElements[i].getAttribute("confirm-message"); - if (optionResult != undefined) { - checkbox.checked = optionResult; + if (optionResult != undefined) { + checkbox.checked = optionResult; - if (reverse) { - optionsElements[i].querySelector("input").checked = !optionResult; - } + if (reverse) { + optionsElements[i].querySelector("input").checked = !optionResult; } + } - // See if anything extra should be run first time - switch (option) { - case "supportInvidious": - invidiousInit(checkbox, option); - break; + // See if anything extra should be run first time + switch (option) { + case "supportInvidious": + invidiousInit(checkbox, option); + break; + } + + // Add click listener + checkbox.addEventListener("click", () => { + // Confirm if required + if (checkbox.checked && confirmMessage && !confirm(chrome.i18n.getMessage(confirmMessage))){ + checkbox.checked = false; + return; } - // Add click listener - checkbox.addEventListener("click", () => { - // Confirm if required - if (checkbox.checked && confirmMessage && !confirm(chrome.i18n.getMessage(confirmMessage))){ - checkbox.checked = false; - return; - } + Config.config[option] = reverse ? !checkbox.checked : checkbox.checked; - Config.config[option] = reverse ? !checkbox.checked : checkbox.checked; - - // See if anything extra must be run - switch (option) { - case "supportInvidious": - invidiousOnClick(checkbox, option); - break; - case "disableAutoSkip": - if (!checkbox.checked) { - // Enable the notice - Config.config["dontShowNotice"] = false; + // See if anything extra must be run + switch (option) { + case "supportInvidious": + invidiousOnClick(checkbox, option); + break; + case "disableAutoSkip": + if (!checkbox.checked) { + // Enable the notice + Config.config["dontShowNotice"] = false; - const showNoticeSwitch = document.querySelector("[sync-option='dontShowNotice'] > label > label > input"); - showNoticeSwitch.checked = true; - } - - break; + const showNoticeSwitch = document.querySelector("[sync-option='dontShowNotice'] > label > label > input"); + showNoticeSwitch.checked = true; } - }); - break; - } - case "text-change": { - const textChangeOption = optionsElements[i].getAttribute("sync-option"); - const textChangeInput = optionsElements[i].querySelector(".option-text-box"); + + break; + } + }); + break; + } + case "text-change": { + const textChangeOption = optionsElements[i].getAttribute("sync-option"); + const textChangeInput = optionsElements[i].querySelector(".option-text-box"); - const textChangeSetButton = optionsElements[i].querySelector(".text-change-set"); + const textChangeSetButton = optionsElements[i].querySelector(".text-change-set"); - textChangeInput.value = Config.config[textChangeOption]; + textChangeInput.value = Config.config[textChangeOption]; + + textChangeSetButton.addEventListener("click", async () => { + // See if anything extra must be done + switch (textChangeOption) { + case "serverAddress": { + const result = validateServerAddress(textChangeInput.value); - textChangeSetButton.addEventListener("click", async () => { - // See if anything extra must be done - switch (textChangeOption) { - case "serverAddress": { - const result = validateServerAddress(textChangeInput.value); - - if (result !== null) { - textChangeInput.value = result; - } else { - return; - } - - // Permission needed on Firefox - if (utils.isFirefox()) { - const permissionSuccess = await new Promise((resolve) => { - chrome.permissions.request({ - origins: [textChangeInput.value + "/"], - permissions: [] - }, resolve); - }); - - if (!permissionSuccess) return; - } - - break; - } + if (result !== null) { + textChangeInput.value = result; + } else { + return; } - Config.config[textChangeOption] = textChangeInput.value; - }); + // Permission needed on Firefox + if (utils.isFirefox()) { + const permissionSuccess = await new Promise((resolve) => { + chrome.permissions.request({ + origins: [textChangeInput.value + "/"], + permissions: [] + }, resolve); + }); - // Reset to the default if needed - const textChangeResetButton = optionsElements[i].querySelector(".text-change-reset"); - textChangeResetButton.addEventListener("click", () => { - if (!confirm(chrome.i18n.getMessage("areYouSureReset"))) return; + if (!permissionSuccess) return; + } - Config.config[textChangeOption] = Config.defaults[textChangeOption]; + break; + } + } - textChangeInput.value = Config.config[textChangeOption]; - }); + Config.config[textChangeOption] = textChangeInput.value; + }); - break; - } - case "private-text-change": { - const button = optionsElements[i].querySelector(".trigger-button"); - button.addEventListener("click", () => activatePrivateTextChange( optionsElements[i])); + // Reset to the default if needed + const textChangeResetButton = optionsElements[i].querySelector(".text-change-reset"); + textChangeResetButton.addEventListener("click", () => { + if (!confirm(chrome.i18n.getMessage("areYouSureReset"))) return; - const privateTextChangeOption = optionsElements[i].getAttribute("sync-option"); - // See if anything extra must be done - switch (privateTextChangeOption) { - case "invidiousInstances": - invidiousInstanceAddInit( optionsElements[i], privateTextChangeOption); - } + Config.config[textChangeOption] = Config.defaults[textChangeOption]; - break; - } - case "button-press": { - const actionButton = optionsElements[i].querySelector(".trigger-button"); + textChangeInput.value = Config.config[textChangeOption]; + }); - switch(optionsElements[i].getAttribute("sync-option")) { - case "copyDebugInformation": - actionButton.addEventListener("click", copyDebugOutputToClipboard); - break; - } + break; + } + case "private-text-change": { + const button = optionsElements[i].querySelector(".trigger-button"); + button.addEventListener("click", () => activatePrivateTextChange( optionsElements[i])); - break; + const privateTextChangeOption = optionsElements[i].getAttribute("sync-option"); + // See if anything extra must be done + switch (privateTextChangeOption) { + case "invidiousInstances": + invidiousInstanceAddInit( optionsElements[i], privateTextChangeOption); } - case "keybind-change": { - const keybindButton = optionsElements[i].querySelector(".trigger-button"); - keybindButton.addEventListener("click", () => activateKeybindChange( optionsElements[i])); + break; + } + case "button-press": { + const actionButton = optionsElements[i].querySelector(".trigger-button"); + + switch(optionsElements[i].getAttribute("sync-option")) { + case "copyDebugInformation": + actionButton.addEventListener("click", copyDebugOutputToClipboard); break; } - case "display":{ - updateDisplayElement( optionsElements[i]) - break; - } - case "number-change": { - const numberChangeOption = optionsElements[i].getAttribute("sync-option"); - const configValue = Config.config[numberChangeOption]; - const numberInput = optionsElements[i].querySelector("input"); - if (isNaN(configValue) || configValue < 0) { - numberInput.value = Config.defaults[numberChangeOption]; - } else { - numberInput.value = configValue; - } - - numberInput.addEventListener("input", () => { - Config.config[numberChangeOption] = numberInput.value; - }); + break; + } + case "keybind-change": { + const keybindButton = optionsElements[i].querySelector(".trigger-button"); + keybindButton.addEventListener("click", () => activateKeybindChange( optionsElements[i])); - break; + break; + } + case "display":{ + updateDisplayElement( optionsElements[i]) + break; + } + case "number-change": { + const numberChangeOption = optionsElements[i].getAttribute("sync-option"); + const configValue = Config.config[numberChangeOption]; + const numberInput = optionsElements[i].querySelector("input"); + + if (isNaN(configValue) || configValue < 0) { + numberInput.value = Config.defaults[numberChangeOption]; + } else { + numberInput.value = configValue; } - case "react-CategoryChooserComponent": - new CategoryChooser(optionsElements[i]); + + numberInput.addEventListener("input", () => { + Config.config[numberChangeOption] = numberInput.value; + }); + + break; + } + case "react-CategoryChooserComponent": + new CategoryChooser(optionsElements[i]); break; } } @@ -208,8 +208,8 @@ function optionsConfigUpdateListener() { for (let i = 0; i < optionsElements.length; i++) { switch (optionsElements[i].getAttribute("option-type")) { - case "display": - updateDisplayElement( optionsElements[i]) + case "display": + updateDisplayElement( optionsElements[i]) } } } @@ -226,9 +226,9 @@ function updateDisplayElement(element: HTMLElement) { // See if anything extra must be run switch (displayOption) { - case "invidiousInstances": - element.innerText = displayText.join(', '); - break; + case "invidiousInstances": + element.innerText = displayText.join(', '); + break; } } @@ -425,24 +425,24 @@ function activatePrivateTextChange(element: HTMLElement) { // See if anything extra must be done switch (option) { - case "invidiousInstances": - element.querySelector(".option-hidden-section").classList.remove("hidden"); - return; + case "invidiousInstances": + element.querySelector(".option-hidden-section").classList.remove("hidden"); + return; } let result = Config.config[option]; // See if anything extra must be done switch (option) { - case "*": { - const jsonData = JSON.parse(JSON.stringify(Config.localConfig)); + case "*": { + const jsonData = JSON.parse(JSON.stringify(Config.localConfig)); - // Fix segmentTimes data as it is destroyed from the JSON stringify - jsonData.segmentTimes = Config.encodeStoredItem(Config.localConfig.segmentTimes); + // Fix segmentTimes data as it is destroyed from the JSON stringify + jsonData.segmentTimes = Config.encodeStoredItem(Config.localConfig.segmentTimes); - result = JSON.stringify(jsonData); - break; - } + result = JSON.stringify(jsonData); + break; + } } textBox.value = result; @@ -455,30 +455,30 @@ function activatePrivateTextChange(element: HTMLElement) { // See if anything extra must be done switch (option) { - case "*": - try { - const newConfig = JSON.parse(textBox.value); - for (const key in newConfig) { - Config.config[key] = newConfig[key]; - } - Config.convertJSON(); - - if (newConfig.supportInvidious) { - const checkbox = document.querySelector("#support-invidious > label > label > input"); + case "*": + try { + const newConfig = JSON.parse(textBox.value); + for (const key in newConfig) { + Config.config[key] = newConfig[key]; + } + Config.convertJSON(); + + if (newConfig.supportInvidious) { + const checkbox = document.querySelector("#support-invidious > label > label > input"); - checkbox.checked = true; - await invidiousOnClick(checkbox, "supportInvidious"); - } + checkbox.checked = true; + await invidiousOnClick(checkbox, "supportInvidious"); + } - window.location.reload(); + window.location.reload(); - } catch (e) { - alert(chrome.i18n.getMessage("incorrectlyFormattedOptions")); - } + } catch (e) { + alert(chrome.i18n.getMessage("incorrectlyFormattedOptions")); + } - break; - default: - Config.config[option] = textBox.value; + break; + default: + Config.config[option] = textBox.value; } } }); @@ -533,10 +533,10 @@ function copyDebugOutputToClipboard() { // Copy object to clipboard navigator.clipboard.writeText(JSON.stringify(output, null, 4)) - .then(() => { - alert(chrome.i18n.getMessage("copyDebugInformationComplete")); - }) - .catch(() => { - alert(chrome.i18n.getMessage("copyDebugInformationFailed")); - }); + .then(() => { + alert(chrome.i18n.getMessage("copyDebugInformationComplete")); + }) + .catch(() => { + alert(chrome.i18n.getMessage("copyDebugInformationFailed")); + }); } diff --git a/src/popup.ts b/src/popup.ts index db34325c9a..82c13b42c4 100644 --- a/src/popup.ts +++ b/src/popup.ts @@ -227,8 +227,8 @@ async function runThePopup(messageListener?: MessageListener): Promise { setTimeout(() => PageElements.sponsorblockPopup.classList.remove("preload"), 250); messageHandler.query({ - active: true, - currentWindow: true + active: true, + currentWindow: true }, onTabs); function onTabs(tabs) { @@ -313,7 +313,7 @@ async function runThePopup(messageListener?: MessageListener): Promise { document.querySelectorAll('.SBWhitelistIcon')[0].classList.add("rotated"); } }); - } + } ); } @@ -374,8 +374,8 @@ async function runThePopup(messageListener?: MessageListener): Promise { // Sort list by start time const segmentTimes = request.sponsorTimes - .sort((a, b) => a.segment[1] - b.segment[1]) - .sort((a, b) => a.segment[0] - b.segment[0]); + .sort((a, b) => a.segment[1] - b.segment[1]) + .sort((a, b) => a.segment[0] - b.segment[0]); //add them as buttons to the issue reporting container const container = document.getElementById("issueReporterTimeButtons"); @@ -655,7 +655,7 @@ async function runThePopup(messageListener?: MessageListener): Promise { message: 'whitelistChange', value: true }); - } + } ); } ); @@ -673,35 +673,35 @@ async function runThePopup(messageListener?: MessageListener): Promise { {message: 'getChannelID'}, function(response) { //get whitelisted channels - let whitelistedChannels = Config.config.whitelistedChannels; - if (whitelistedChannels == undefined) { - whitelistedChannels = []; - } + let whitelistedChannels = Config.config.whitelistedChannels; + if (whitelistedChannels == undefined) { + whitelistedChannels = []; + } + + //remove this channel + const index = whitelistedChannels.indexOf(response.channelID); + whitelistedChannels.splice(index, 1); + + //change button + PageElements.whitelistChannel.style.display = "unset"; + PageElements.unwhitelistChannel.style.display = "none"; + document.querySelectorAll('.SBWhitelistIcon')[0].classList.remove("rotated"); - //remove this channel - const index = whitelistedChannels.indexOf(response.channelID); - whitelistedChannels.splice(index, 1); - - //change button - PageElements.whitelistChannel.style.display = "unset"; - PageElements.unwhitelistChannel.style.display = "none"; - document.querySelectorAll('.SBWhitelistIcon')[0].classList.remove("rotated"); - - //save this - Config.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 - }); - } - ); + //save this + Config.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 + }); + } + ); } ); }); @@ -711,7 +711,7 @@ async function runThePopup(messageListener?: MessageListener): Promise { * Should skipping be disabled (visuals stay) */ function toggleSkipping(disabled) { - Config.config.disableSkipping = disabled; + Config.config.disableSkipping = disabled; let hiddenButton = PageElements.disableSkipping; let shownButton = PageElements.enableSkipping; diff --git a/src/render/CategoryChooser.tsx b/src/render/CategoryChooser.tsx index eab9edf6f3..9f447dd090 100644 --- a/src/render/CategoryChooser.tsx +++ b/src/render/CategoryChooser.tsx @@ -12,4 +12,4 @@ class CategoryChooser { } } -export default CategoryChooser; \ No newline at end of file +export default CategoryChooser; diff --git a/src/render/SkipNotice.tsx b/src/render/SkipNotice.tsx index ac66cae4d1..81e9d88e37 100644 --- a/src/render/SkipNotice.tsx +++ b/src/render/SkipNotice.tsx @@ -70,4 +70,4 @@ class SkipNotice { } } -export default SkipNotice; \ No newline at end of file +export default SkipNotice; diff --git a/src/render/SubmissionNotice.tsx b/src/render/SubmissionNotice.tsx index 8c267854a5..8a4c469c3f 100644 --- a/src/render/SubmissionNotice.tsx +++ b/src/render/SubmissionNotice.tsx @@ -63,4 +63,4 @@ class SubmissionNotice { } } -export default SubmissionNotice; \ No newline at end of file +export default SubmissionNotice; diff --git a/src/utils.ts b/src/utils.ts index 2331b2d401..0c493be0d3 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -229,17 +229,17 @@ class Utils { const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; let result = ""; if (window.crypto && window.crypto.getRandomValues) { - const values = new Uint32Array(length); - window.crypto.getRandomValues(values); - for (let i = 0; i < length; i++) { - result += charset[values[i] % charset.length]; - } - return result; + const values = new Uint32Array(length); + window.crypto.getRandomValues(values); + for (let i = 0; i < length; i++) { + result += charset[values[i] % charset.length]; + } + return result; } else { - for (let i = 0; i < length; i++) { - result += charset[Math.floor(Math.random() * charset.length)]; - } - return result; + for (let i = 0; i < length; i++) { + result += charset[Math.floor(Math.random() * charset.length)]; + } + return result; } } diff --git a/webpack/webpack.common.js b/webpack/webpack.common.js index 79fea60b01..304a655fc8 100644 --- a/webpack/webpack.common.js +++ b/webpack/webpack.common.js @@ -36,16 +36,16 @@ module.exports = env => ({ plugins: [ // exclude locale files in moment new CopyPlugin({ - patterns: [ - { - from: '.', - to: '../', - globOptions: { - ignore: ['manifest.json'], - }, - context: './public', - } - ] + patterns: [ + { + from: '.', + to: '../', + globOptions: { + ignore: ['manifest.json'], + }, + context: './public', + } + ] }), new BuildManifest({ browser: env.browser, diff --git a/webpack/webpack.dev.js b/webpack/webpack.dev.js index 6ceabb8377..4e549d8824 100644 --- a/webpack/webpack.dev.js +++ b/webpack/webpack.dev.js @@ -4,4 +4,4 @@ const common = require('./webpack.common.js'); module.exports = env => merge(common(env), { devtool: 'inline-source-map', mode: 'development' -}); \ No newline at end of file +}); diff --git a/webpack/webpack.manifest.js b/webpack/webpack.manifest.js index 63c82cdf89..1369c5fb5c 100644 --- a/webpack/webpack.manifest.js +++ b/webpack/webpack.manifest.js @@ -77,4 +77,4 @@ function mergeObjects(object1, object2) { } } -module.exports = BuildManifest; \ No newline at end of file +module.exports = BuildManifest; diff --git a/webpack/webpack.prod.js b/webpack/webpack.prod.js index 65be616f1e..1ff6b77f43 100644 --- a/webpack/webpack.prod.js +++ b/webpack/webpack.prod.js @@ -8,4 +8,4 @@ module.exports = env => { return merge(common(env), { mode }); -}; \ No newline at end of file +};