From da80996a8c3c0c26fe6546e55c2744fb4e3a71b9 Mon Sep 17 00:00:00 2001 From: shinigami-eyes <43276258+shinigami-eyes@users.noreply.github.com> Date: Sat, 29 Jun 2019 19:47:05 +0200 Subject: [PATCH] Fix more type errors, refactor a few things --- extension/background.ts | 99 ++++----- extension/content.ts | 383 +++++++++++++++-------------------- extension/definitions.d.ts | 37 +++- extension/tsconfig.json | 4 +- extension/webextensions.d.ts | 23 ++- 5 files changed, 263 insertions(+), 283 deletions(-) diff --git a/extension/background.ts b/extension/background.ts index 5837fa0..bdb1e56 100644 --- a/extension/background.ts +++ b/extension/background.ts @@ -3,7 +3,7 @@ const PENDING_SUBMISSIONS = ':PENDING_SUBMISSIONS' const MIGRATION = ':MIGRATION' -const CURRENT_VERSION = 100018; +const CURRENT_VERSION = 100019; // If a user labels one of these URLs, they're making a mistake. Ignore the label. // This list includes: @@ -11,7 +11,7 @@ const CURRENT_VERSION = 100018; // * System pages of supported social networks // * Archival and link shortening sites. // * Reddit bots. -var badIdentifiersArray = [ +const badIdentifiersArray = [ 'archive.is', 'archive.org', 'assets.tumblr.com', @@ -232,12 +232,12 @@ var badIdentifiersArray = [ 'youtube.com/redirect', 'youtube.com/watch', ]; -var badIdentifiers : {[id: string]: true} = {}; +const badIdentifiers : {[id: string]: true} = {}; badIdentifiersArray.forEach(x => badIdentifiers[x] = true); -var lastSubmissionError = null; +var lastSubmissionError : string = null; -var needsInfiniteResubmissionWorkaround = [ +const needsInfiniteResubmissionWorkaround = [ '046775268347','094745034139','059025030493','016970595453','016488055088','028573603939', '047702135398','035965787127','069722626647','044482561296','068530257405','071378971311', '050784255720','074169481269','001621982155','014636303566','016313013148','051923868290', @@ -246,10 +246,10 @@ var needsInfiniteResubmissionWorkaround = [ '040689448048','048816243838','018152001078','059285890303','073205501344','096068619182' ] -var overrides = null; +var overrides : LabelMap = null; var accepted = false; -var installationId = null; +var installationId : string = null; browser.storage.local.get(['overrides', 'accepted', 'installationId'], v => { if (!v.installationId) { @@ -262,17 +262,17 @@ browser.storage.local.get(['overrides', 'accepted', 'installationId'], v => { accepted = v.accepted overrides = v.overrides || {} - var migration = overrides[MIGRATION] || 0; - if(migration < CURRENT_VERSION){ + const migration = overrides[MIGRATION] || 0; + if (migration < CURRENT_VERSION){ - for(var key of Object.getOwnPropertyNames(overrides)){ - if(key.startsWith(':')) continue; - if(key.startsWith('facebook.com/a.')){ + for (const key of Object.getOwnPropertyNames(overrides)){ + if (key.startsWith(':')) continue; + if (key.startsWith('facebook.com/a.')){ delete overrides[key]; continue; } - if(key != key.toLowerCase()){ - var v = overrides[key]; + if (key != key.toLowerCase()){ + let v = overrides[key]; delete overrides[key]; overrides[key.toLowerCase()] = v; } @@ -281,30 +281,29 @@ browser.storage.local.get(['overrides', 'accepted', 'installationId'], v => { badIdentifiersArray.forEach(x => delete overrides[x]); if (needsInfiniteResubmissionWorkaround.indexOf(installationId.substring(0, 12)) != -1) - overrides[PENDING_SUBMISSIONS] = []; - overrides[MIGRATION] = CURRENT_VERSION; + overrides[PENDING_SUBMISSIONS] = []; + overrides[MIGRATION] = CURRENT_VERSION; browser.storage.local.set({ overrides: overrides }); } }) const bloomFilters : BloomFilter[] = []; -function loadBloomFilter(name: string) { +async function loadBloomFilter(name: LabelKind) { - var url = browser.extension.getURL('data/' + name + '.dat'); - fetch(url).then(response => { - response.arrayBuffer().then(arrayBuffer => { - var array = new Uint32Array(arrayBuffer); - var b = new BloomFilter(array, 20); - b.name = name; - bloomFilters.push(b); - }); - }); + const url = browser.extension.getURL('data/' + name + '.dat'); + const response = await fetch(url); + const arrayBuffer = await response.arrayBuffer(); + + const array = new Uint32Array(arrayBuffer); + const b = new BloomFilter(array, 20); + b.name = name; + bloomFilters.push(b); } -browser.runtime.onMessage.addListener((message, sender, sendResponse) => { +browser.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.acceptClicked !== undefined) { accepted = message.acceptClicked; browser.storage.local.set({ accepted: accepted }); @@ -314,22 +313,22 @@ browser.runtime.onMessage.addListener((message, sender, sendResponse) => { uncommittedResponse = null; return; } - var response = {}; - var transphobic = message.myself && bloomFilters.filter(x => x.name == 'transphobic')[0].test(message.myself); - for (var id of message.ids) { + const response : LabelMap = {}; + const transphobic = message.myself && bloomFilters.filter(x => x.name == 'transphobic')[0].test(message.myself); + for (const id of message.ids) { if (overrides[id] !== undefined) { response[id] = overrides[id]; continue; } if (transphobic) { if (id == message.myself) continue; - var sum = 0; - for (var i = 0; i < id.length; i++) { + let sum = 0; + for (let i = 0; i < id.length; i++) { sum += id.charCodeAt(i); } if (sum % 8 != 0) continue; } - for (var bloomFilter of bloomFilters) { + for (const bloomFilter of bloomFilters) { if (bloomFilter.test(id)) response[id] = bloomFilter.name; } } @@ -368,11 +367,11 @@ createContextMenu('Mark as t-friendly', 'mark-t-friendly'); createContextMenu('Clear', 'mark-none'); createContextMenu('Help', 'help'); -var uncommittedResponse = null; +var uncommittedResponse : ShinigamiEyesSubmission = null; async function submitPendingRatings() { - var submitted = overrides[PENDING_SUBMISSIONS].map(x => x); - var requestBody = { + const submitted = getPendingSubmissions().map(x => x); + const requestBody = { installationId: installationId, lastError: lastSubmissionError, entries: submitted @@ -380,17 +379,17 @@ async function submitPendingRatings() { lastSubmissionError = null; console.log('Sending request'); try { - var response = await fetch('https://k5kk18774h.execute-api.us-east-1.amazonaws.com/default/shinigamiEyesSubmission', { + const response = await fetch('https://k5kk18774h.execute-api.us-east-1.amazonaws.com/default/shinigamiEyesSubmission', { body: JSON.stringify(requestBody), method: 'POST', credentials: 'omit', }); if (response.status != 200) throw ('HTTP status: ' + response.status) - var result = await response.text(); + const result = await response.text(); if (result != 'SUCCESS') throw 'Bad response: ' + ('' + result).substring(0, 20); - overrides[PENDING_SUBMISSIONS] = overrides[PENDING_SUBMISSIONS].filter(x => submitted.indexOf(x) == -1); + overrides[PENDING_SUBMISSIONS] = getPendingSubmissions().filter(x => submitted.indexOf(x) == -1); browser.storage.local.set({ overrides: overrides }); } catch(e) { lastSubmissionError = '' + e @@ -398,12 +397,16 @@ async function submitPendingRatings() { } +function getPendingSubmissions() : ShinigamiEyesSubmission[]{ + return overrides[PENDING_SUBMISSIONS]; +} -function saveLabel(response) { + +function saveLabel(response: ShinigamiEyesSubmission) { if (accepted) { - if (!overrides[PENDING_SUBMISSIONS]) { - overrides[PENDING_SUBMISSIONS] = Object.getOwnPropertyNames(overrides) - .map(x => { return { identifier: x, label: overrides[x] } }); + if (!getPendingSubmissions()) { + overrides[PENDING_SUBMISSIONS] = Object.getOwnPropertyNames(overrides) + .map(x => { return { identifier: x, label: overrides[x] } }); } overrides[response.identifier] = response.mark; if (response.secondaryIdentifier) @@ -411,7 +414,7 @@ function saveLabel(response) { browser.storage.local.set({ overrides: overrides }); response.version = CURRENT_VERSION; response.submissionId = (Math.random() + '').replace('.', ''); - overrides[PENDING_SUBMISSIONS].push(response); + getPendingSubmissions().push(response); submitPendingRatings(); //console.log(response); browser.tabs.sendMessage(response.tabId, { updateAllLabels: true }); @@ -436,18 +439,18 @@ browser.contextMenus.onClicked.addListener(function (info, tab) { return; } - var tabId = tab.id; - var frameId = info.frameId; + const tabId = tab.id; + const frameId = info.frameId; var label = info.menuItemId.substring('mark-'.length); if (label == 'none') label = ''; - browser.tabs.sendMessage(tabId, { + browser.tabs.sendMessage(tabId, { mark: label, url: info.linkUrl, tabId: tabId, frameId: frameId, // elementId: info.targetElementId, - debug: overrides.debug + debug: overrides.debug }, { frameId: frameId }, response => { if (!response.identifier) return; if (response.mark){ diff --git a/extension/content.ts b/extension/content.ts index c5bbf51..f89a7bc 100644 --- a/extension/content.ts +++ b/extension/content.ts @@ -8,113 +8,88 @@ if (hostname.endsWith('.reddit.com')) hostname = 'reddit.com'; if (hostname.endsWith('.facebook.com')) hostname = 'facebook.com'; if (hostname.endsWith('.youtube.com')) hostname = 'youtube.com'; - -var myself = null; +var myself : string = null; function fixupSiteStyles() { - if (hostname == 'reddit.com') { - myself = document.querySelector('#header-bottom-right .user a'); + if (hostname == 'facebook.com') { + let m = document.querySelector("[id^='profile_pic_header_']") + if (m) myself = 'facebook.com/' + captureRegex(m.id, /header_(\d+)/); + } else if (hostname == 'medium.com') { + addStyleSheet(` + a.show-thread-link, a.ThreadedConversation-moreRepliesLink { + color: inherit !important; + } + .fullname, + .stream-item a:hover .fullname, + .stream-item a:active .fullname + {color:inherit;} + `); + } else if (domainIs(hostname, 'tumblr.com')) { + addStyleSheet(` + .assigned-label-transphobic { outline: 2px solid #991515 !important; } + .assigned-label-t-friendly { outline: 1px solid #77B91E !important; } + `); + } else if (hostname.indexOf('wiki') != -1){ + addStyleSheet(` + .assigned-label-transphobic { outline: 1px solid #991515 !important; } + .assigned-label-t-friendly { outline: 1px solid #77B91E !important; } + `); + } else if (hostname == 'twitter.com') { + myself = getIdentifier(document.querySelector('.DashUserDropdown-userInfo a')); + addStyleSheet(` + .pretty-link b, .pretty-link s { + color: inherit !important; + } + + a.show-thread-link, a.ThreadedConversation-moreRepliesLink { + color: inherit !important; + } + .fullname, + .stream-item a:hover .fullname, + .stream-item a:active .fullname + {color:inherit;} + `); + } else if (hostname == 'reddit.com') { + myself = getIdentifier(document.querySelector('#header-bottom-right .user a')); if (!myself) { - var m : any = document.querySelector('#USER_DROPDOWN_ID'); + let m = document.querySelector('#USER_DROPDOWN_ID'); if (m) { - m = [...m.querySelectorAll('*')].filter(x => x.childNodes.length == 1 && x.firstChild.nodeType == 3).map(x => x.textContent)[0] - if (m) myself = 'reddit.com/user/' + m; + let username = [...m.querySelectorAll('*')].filter(x => x.childNodes.length == 1 && x.firstChild.nodeType == 3).map(x => x.textContent)[0] + if (username) myself = 'reddit.com/user/' + username; } } - } - if (hostname == 'facebook.com') { - var m : any = document.querySelector("[id^='profile_pic_header_']") - if (m) myself = 'facebook.com/' + captureRegex(m.id, /header_(\d+)/); - } - if (hostname == 'medium.com') { - - - var style = document.createElement('style'); - style.textContent = ` - - a.show-thread-link, a.ThreadedConversation-moreRepliesLink { - color: inherit !important; - } - .fullname, - .stream-item a:hover .fullname, - .stream-item a:active .fullname - {color:inherit;} - - `; - document.head.appendChild(style); - - } - if (isHostedOn(hostname, 'tumblr.com')) { - var style = document.createElement('style'); - style.textContent = ` - .assigned-label-transphobic { outline: 2px solid #991515 !important; } - .assigned-label-t-friendly { outline: 1px solid #77B91E !important; } - `; - document.head.appendChild(style); - } - if(hostname.indexOf('wiki') != -1){ - var style = document.createElement('style'); - style.textContent = ` - .assigned-label-transphobic { outline: 1px solid #991515 !important; } - .assigned-label-t-friendly { outline: 1px solid #77B91E !important; } - - `; - document.head.appendChild(style); - } - if (hostname == 'twitter.com') { - myself = document.querySelector('.DashUserDropdown-userInfo a'); - - var style = document.createElement('style'); - style.textContent = ` - - .pretty-link b, .pretty-link s { - color: inherit !important; - } - - a.show-thread-link, a.ThreadedConversation-moreRepliesLink { - color: inherit !important; - } - .fullname, - .stream-item a:hover .fullname, - .stream-item a:active .fullname - {color:inherit;} - - `; - document.head.appendChild(style); - - } else if (hostname == 'reddit.com') { - var style = document.createElement('style'); - style.textContent = ` - .author { color: #369 !important;} - `; - document.head.appendChild(style); + addStyleSheet(` + .author { color: #369 !important;} + `); } } +function addStyleSheet(css: string){ + const style = document.createElement('style'); + style.textContent = css; + document.head.appendChild(style); +} + function maybeDisableCustomCss() { - var shouldDisable = null; + var shouldDisable: (s: {ownerNode: HTMLElement}) => boolean = null; if (hostname == 'twitter.com') shouldDisable = x => x.ownerNode && x.ownerNode.id && x.ownerNode.id.startsWith('user-style'); else if (hostname == 'medium.com') shouldDisable = x => x.ownerNode && x.ownerNode.className && x.ownerNode.className == 'js-collectionStyle'; else if (hostname == 'disqus.com') shouldDisable = x => x.ownerNode && x.ownerNode.id && x.ownerNode.id.startsWith('css_'); if (shouldDisable) - [...document.styleSheets].filter(shouldDisable).forEach(x => x.disabled = true); + [...document.styleSheets].filter(shouldDisable).forEach(x => x.disabled = true); } function init() { fixupSiteStyles(); - if (isHostedOn(hostname, 'youtube.com')) { + if (domainIs(hostname, 'youtube.com')) { setInterval(updateYouTubeChannelHeader, 300); setInterval(updateAllLabels, 6000); } - if (myself && (myself.href || myself.startsWith('http:') || myself.startsWith('https:'))) - myself = getIdentifier(myself); console.log('Self: ' + myself) - - maybeDisableCustomCss(); updateAllLabels(); @@ -135,7 +110,6 @@ function init() { } } solvePendingLabels(); - }); observer.observe(document.body, { @@ -143,15 +117,14 @@ function init() { subtree: true }); - document.addEventListener('contextmenu', evt => { - lastRightClickedElement = evt.target; + lastRightClickedElement = evt.target; }, true); } -var lastRightClickedElement = null; -var lastAppliedYouTubeUrl = null; -var lastAppliedYouTubeTitle = null; +var lastRightClickedElement : HTMLElement = null; +var lastAppliedYouTubeUrl : string = null; +var lastAppliedYouTubeTitle : string = null; function updateYouTubeChannelHeader() { var url = window.location.href; @@ -187,24 +160,21 @@ function updateYouTubeChannelHeader() { function updateAllLabels(refresh?: boolean) { if (refresh) knownLabels = {}; - var links = document.links; - for (var i = 0; i < links.length; i++) { - var a = links[i]; + for (const a of document.getElementsByTagName('a')) { initLink(a); } solvePendingLabels(); } +var knownLabels: LabelMap = {}; -var knownLabels = {}; - -var labelsToSolve = []; +var labelsToSolve : LabelToSolve[] = []; function solvePendingLabels() { if (!labelsToSolve.length) return; var uniqueIdentifiers = Array.from(new Set(labelsToSolve.map(x => x.identifier))); var tosolve = labelsToSolve; labelsToSolve = []; - browser.runtime.sendMessage({ ids: uniqueIdentifiers, myself: myself }, response => { + browser.runtime.sendMessage({ ids: uniqueIdentifiers, myself: myself }, (response: LabelMap) => { for (const item of tosolve) { var label = response[item.identifier]; knownLabels[item.identifier] = label || ''; @@ -213,8 +183,7 @@ function solvePendingLabels() { }); } -function applyLabel(a, identifier) { - +function applyLabel(a: HTMLAnchorElement, identifier: string) { if (a.assignedCssLabel) { a.classList.remove('assigned-label-' + a.assignedCssLabel); a.classList.remove('has-assigned-label'); @@ -246,38 +215,26 @@ function initLink(a: HTMLAnchorElement) { applyLabel(a, identifier); } -function isHostedOn(/** @type {string}*/fullHost, /** @type {string}*/baseHost) { - if (baseHost.length > fullHost.length) return false; - if (baseHost.length == fullHost.length) return baseHost == fullHost; - var k = fullHost.charCodeAt(fullHost.length - baseHost.length - 1); - if (k == 0x2E) return fullHost.endsWith(baseHost); +function domainIs(host: string, baseDomain: string) { + if (baseDomain.length > host.length) return false; + if (baseDomain.length == host.length) return baseDomain == host; + var k = host.charCodeAt(host.length - baseDomain.length - 1); + if (k == 0x2E /* . */) return host.endsWith(baseDomain); else return false; } -function getQuery(search: string) : any { - if (!search) return {}; - var s = {}; - if (search.startsWith('?')) search = search.substring(1); - for (var pair of search.split('&')) { - var z = pair.split('='); - if (z.length != 2) continue; - s[decodeURIComponent(z[0]).replace(/\+/g, ' ')] = decodeURIComponent(z[1].replace(/\+/g, ' ')); - } - return s; -} - -function takeFirstPathComponents(path: string, num: number) { +function getPartialPath(path: string, num: number) { var m = path.split('/') m = m.slice(1, 1 + num); if (m.length && !m[m.length - 1]) m.length--; if (m.length != num) return '!!' return '/' + m.join('/'); } -function takeNthPathComponent(path: string, nth: number) { - return path.split('/')[nth + 1] || null; +function getPathPart(path: string, index: number) { + return path.split('/')[index + 1] || null; } -function captureRegex(str, regex) { +function captureRegex(str: string, regex: RegExp) { if (!str) return null; var match = str.match(regex); if (match && match[1]) return match[1]; @@ -307,30 +264,29 @@ function getCurrentFacebookPageId() { return null; } -function getIdentifier(urlstr) { +function getIdentifier(link: string | HTMLAnchorElement) { try { - var k = getIdentifierInternal(urlstr); + var k = link instanceof Node ? getIdentifierFromElementImpl(link) : getIdentifierFromURLImpl(tryParseURL(link)); if (!k || k.indexOf('!') != -1) return null; return k.toLowerCase(); } catch (e) { - console.warn("Unable to get identifier for " + urlstr); + console.warn("Unable to get identifier for " + link); return null; } } -function getIdentifierInternal(urlstr) { - if (!urlstr) return null; +function getIdentifierFromElementImpl(element: HTMLAnchorElement): string { + if (!element) return null; + + const dataset = element.dataset; if (hostname == 'reddit.com') { - const parent = urlstr.parentElement; - if (parent && parent.classList.contains('domain') && urlstr.textContent.startsWith('self.')) return null; - } - if (hostname == 'disqus.com') { - if (urlstr.classList && urlstr.classList.contains('time-ago')) return null; - } - - if (hostname == 'facebook.com' && urlstr.tagName) { - const parent = urlstr.parentElement; + const parent = element.parentElement; + if (parent && parent.classList.contains('domain') && element.textContent.startsWith('self.')) return null; + } else if (hostname == 'disqus.com') { + if (element.classList && element.classList.contains('time-ago')) return null; + } else if (hostname == 'facebook.com') { + const parent = element.parentElement; if (parent && (parent.tagName == 'H1' || parent.id == 'fb-timeline-cover-name')) { const id = getCurrentFacebookPageId(); //console.log('Current fb page: ' + id) @@ -339,17 +295,17 @@ function getIdentifierInternal(urlstr) { } // comment timestamp - if (urlstr.firstChild && urlstr.firstChild.tagName == 'ABBR' && urlstr.lastChild == urlstr.firstChild) return null; + if (element.firstChild && (element.firstChild).tagName == 'ABBR' && element.lastChild == element.firstChild) return null; // post 'see more' - if (urlstr.classList.contains('see_more_link')) return null; + if (element.classList.contains('see_more_link')) return null; // post 'continue reading' if (parent && parent.classList.contains('text_exposed_link')) return null; - if (urlstr.dataset) { - const hovercard = urlstr.dataset.hovercard; + if (dataset) { + const hovercard = dataset.hovercard; if (hovercard) { const id = captureRegex(hovercard, /id=(\d+)/); if (id) @@ -357,18 +313,18 @@ function getIdentifierInternal(urlstr) { } // post Comments link - if (urlstr.dataset.testid == 'UFI2CommentsCount/root') return null; + if (dataset.testid == 'UFI2CommentsCount/root') return null; // post Comments link - if (urlstr.dataset.commentPreludeRef) return null; + if (dataset.commentPreludeRef) return null; // page left sidebar - if (urlstr.dataset.endpoint) return null; + if (dataset.endpoint) return null; // profile tabs - if (urlstr.dataset.tabKey) return null; + if (dataset.tabKey) return null; - const gt = urlstr.dataset.gt; + const gt = dataset.gt; if (gt) { const gtParsed = JSON.parse(gt); if (gtParsed.engagement && gtParsed.engagement.eng_tid) { @@ -377,9 +333,9 @@ function getIdentifierInternal(urlstr) { } // comment interaction buttons - if (urlstr.dataset.sigil) return null; + if (dataset.sigil) return null; - let p = urlstr; + let p = element; while (p) { const bt = p.dataset.bt; if (bt) { @@ -390,109 +346,98 @@ function getIdentifierInternal(urlstr) { } } } - if (urlstr.dataset && urlstr.dataset.expandedUrl) urlstr = urlstr.dataset.expandedUrl; - if (urlstr.href !== undefined) urlstr = urlstr.href; + if (dataset && dataset.expandedUrl) return getIdentifierFromURLImpl(tryParseURL(dataset.expandedUrl)); + const href = element.href; + if(href && !href.endsWith('#')) return getIdentifierFromURLImpl(tryParseURL(href)); + return null; +} + +function tryParseURL(urlstr: string){ if (!urlstr) return null; - if (urlstr.endsWith('#')) return null; - let url; try { - url = new URL(urlstr); - } catch (e) { + const url = new URL(urlstr); + if (url.protocol != 'http:' && url.protocol != 'https:') return null; + return url; + } catch(e) { return null; } - if (url.protocol != 'http:' && url.protocol != 'https:') return null; +} + +function getIdentifierFromURLImpl(url: URL): string{ + if(!url) return null; + + // nested urls + if (url.href.indexOf('http', 1) != -1) { + if (url.pathname.startsWith('/intl/')) return null; // facebook language switch links + + // const values = url.searchParams.values() + // HACK: values(...) is not iterable on facebook (babel polyfill?) + const values = url.search.split('&').map(x => { + const eq = x.indexOf('='); + return eq == -1 ? '' : decodeURIComponent(x.substr(eq + 1)); + }); + + for (const value of values) { + if (value.startsWith('http:') || value.startsWith('https:')) { + return getIdentifierFromURLImpl(tryParseURL(value)); + } + } + const newurl = tryParseURL(url.href.substring(url.href.indexOf('http', 1))); + if(newurl) return getIdentifierFromURLImpl(newurl); + } // fb group member badge if (url.pathname.includes('/badge_member_list/')) return null; - if (url.href.indexOf('http', 1) != -1) { - const s = getQuery(url.search); - urlstr = null; - for (const key in s) { - if (s.hasOwnProperty(key)) { - const element = s[key]; - if (element.startsWith('http:') || element.startsWith('https')) { - urlstr = element; - break; - } - } - } - if (urlstr == null) { - urlstr = url.href.substring(url.href.indexOf('http', 1)) - } - try { - url = new URL(urlstr); - } catch (e) { } - } - let host = url.hostname; - if (isHostedOn(host, 'web.archive.org')) { + if (domainIs(host, 'web.archive.org')) { const match = captureRegex(url.href, /\/web\/\w+\/(.*)/); if (!match) return null; - return getIdentifierInternal('http://' + match); + return getIdentifierFromURLImpl(tryParseURL('http://' + match)); } - if (url.search && url.search.includes('http')) { - if (url.pathname.startsWith('/intl/')) return null; // facebook language switch links - for (const q of url.searchParams) { - if (q[1].startsWith('http')) return getIdentifierInternal(q[1]); - } - } - /* - if(host == 't.umblr.com'){ - return getIdentifierInternal(url.searchParams.get('z')); - } - */ + if (host.startsWith('www.')) host = host.substring(4); - if (isHostedOn(host, 'facebook.com')) { - const s = getQuery(url.search); + if (domainIs(host, 'facebook.com')) { + const fbId = url.searchParams.get('id'); const p = url.pathname.replace('/pg/', '/'); - return 'facebook.com/' + (s.id || takeFirstPathComponents(p, p.startsWith('/groups/') ? 2 : 1).substring(1)); - } - if (isHostedOn(host, 'reddit.com')) { + return 'facebook.com/' + (fbId || getPartialPath(p, p.startsWith('/groups/') ? 2 : 1).substring(1)); + } else if (domainIs(host, 'reddit.com')) { const pathname = url.pathname.replace('/u/', '/user/'); if (!pathname.startsWith('/user/') && !pathname.startsWith('/r/')) return null; if(pathname.includes('/comments/') && hostname == 'reddit.com') return null; - return 'reddit.com' + takeFirstPathComponents(pathname, 2); - } - if (isHostedOn(host, 'twitter.com')) { - return 'twitter.com' + takeFirstPathComponents(url.pathname, 1); - } - if (isHostedOn(host, 'youtube.com')) { + return 'reddit.com' + getPartialPath(pathname, 2); + } else if (domainIs(host, 'twitter.com')) { + return 'twitter.com' + getPartialPath(url.pathname, 1); + } else if (domainIs(host, 'youtube.com')) { const pathname = url.pathname.replace('/c/', '/user/'); if (!pathname.startsWith('/user/') && !pathname.startsWith('/channel/')) return null; - return 'youtube.com' + takeFirstPathComponents(pathname, 2); - } - if (isHostedOn(host, 'disqus.com') && url.pathname.startsWith('/by/')) { - return 'disqus.com' + takeFirstPathComponents(url.pathname, 2); - } - if (isHostedOn(host, 'medium.com')) { - return 'medium.com' + takeFirstPathComponents(url.pathname.replace('/t/', '/'), 1); - } - if (isHostedOn(host, 'tumblr.com')) { + return 'youtube.com' + getPartialPath(pathname, 2); + } else if (domainIs(host, 'disqus.com') && url.pathname.startsWith('/by/')) { + return 'disqus.com' + getPartialPath(url.pathname, 2); + } else if (domainIs(host, 'medium.com')) { + return 'medium.com' + getPartialPath(url.pathname.replace('/t/', '/'), 1); + } else if (domainIs(host, 'tumblr.com')) { if (url.pathname.startsWith('/register/follow/')) { - const name = takeNthPathComponent(url.pathname, 2); + const name = getPathPart(url.pathname, 2); return name ? name + '.tumblr.com' : null; } if (host != 'www.tumblr.com' && host != 'assets.tumblr.com' && host.indexOf('.media.') == -1) { if (!url.pathname.startsWith('/tagged/')) return url.host; } return null; - } - if (isHostedOn(host, 'wikipedia.org') || isHostedOn(host, 'rationalwiki.org')) { + } else if (domainIs(host, 'wikipedia.org') || domainIs(host, 'rationalwiki.org')) { if (url.hash || url.pathname.includes(':')) return null; - if (url.pathname.startsWith('/wiki/')) return 'wikipedia.org' + takeFirstPathComponents(url.pathname, 2); + if (url.pathname.startsWith('/wiki/')) return 'wikipedia.org' + getPartialPath(url.pathname, 2); else return null; - } - if (host.indexOf('.blogspot.') != -1) { + } else if (host.indexOf('.blogspot.') != -1) { const m = captureRegex(host, /([a-zA-Z0-9\-]*)\.blogspot/); if (m) return m + '.blogspot.com'; + else return null; + } else { + if (host.startsWith('m.')) host = host.substr(2); + return host; } - - let id = host; - if (id.startsWith('www.')) id = id.substr(4); - if (id.startsWith('m.')) id = id.substr(2); - return id; } @@ -500,7 +445,7 @@ init(); var lastGeneratedLinkId = 0; -function getSnippet(node){ +function getSnippet(node: HTMLElement){ while (node) { var classList = node.classList; if (hostname == 'facebook.com' && node.dataset && node.dataset.ftr) return node; @@ -517,7 +462,7 @@ function getSnippet(node){ } -browser.runtime.onMessage.addListener((message, sender, sendResponse) => { +browser.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.updateAllLabels) { updateAllLabels(true); @@ -528,13 +473,13 @@ browser.runtime.onMessage.addListener((message, sender, sendResponse) => { var target = lastRightClickedElement; // message.elementId ? browser.menus.getTargetElement(message.elementId) : null; while(target){ - if(target.href) break; + if((target).href) break; target = target.parentElement; } - if (target && target.href != message.url) target = null; + if (target && (target).href != message.url) target = null; - var identifier = target ? getIdentifier(target) : getIdentifier(message.url); + var identifier = target ? getIdentifier(target) : getIdentifier(message.url); if (!identifier) return; message.identifier = identifier; @@ -545,7 +490,7 @@ browser.runtime.onMessage.addListener((message, sender, sendResponse) => { message.linkId = ++lastGeneratedLinkId; if (target) - target.setAttribute('shinigami-eyes-link-id', lastGeneratedLinkId); + target.setAttribute('shinigami-eyes-link-id', '' + lastGeneratedLinkId); message.snippet = snippet ? snippet.outerHTML : null; var debugClass = 'shinigami-eyes-debug-snippet-highlight'; diff --git a/extension/definitions.d.ts b/extension/definitions.d.ts index f488813..c049b48 100644 --- a/extension/definitions.d.ts +++ b/extension/definitions.d.ts @@ -1,16 +1,39 @@ declare class BloomFilter { constructor(data: Uint32Array, k: number); test(key: string): boolean; - name: string; + name: LabelKind; +} +interface HTMLElement { + assignedCssLabel?: string +} +interface LabelToSolve { + element: HTMLAnchorElement + identifier: string } type LabelKind = 't-friendly' | 'transphobic' | 'none' | ''; -type ShinigamiSubmission = { - mark: LabelKind - url: string - tabId: number - frameId: number - debug: number +interface ShinigamiEyesSubmission { + mark?: LabelKind + url?: string + tabId?: number + frameId?: number + debug?: number identifier?: string secondaryIdentifier?: string + version?: number + submissionId?: string + contextPage?: string + linkId?: number + snippet?: string } +interface ShinigamiEyesCommand { + acceptClicked?: boolean + myself?: string + ids?: string[] + updateAllLabels?: boolean +} +type LabelMap = {[identifier: string]: LabelKind}; + +interface ShinigamiEyesMessage extends ShinigamiEyesSubmission, ShinigamiEyesCommand{ +} + type ContextMenuCommand = 'mark-t-friendly' | 'mark-transphobic' | 'mark-none' | 'help'; diff --git a/extension/tsconfig.json b/extension/tsconfig.json index 0c51bfe..0989a9a 100644 --- a/extension/tsconfig.json +++ b/extension/tsconfig.json @@ -1,5 +1,7 @@ { "compilerOptions": { - "target": "es2017" + "target": "es2017", + "noImplicitAny": true, + "noImplicitReturns": true, } } \ No newline at end of file diff --git a/extension/webextensions.d.ts b/extension/webextensions.d.ts index ab73e25..539a8a2 100644 --- a/extension/webextensions.d.ts +++ b/extension/webextensions.d.ts @@ -1,9 +1,9 @@ declare type Browser = { runtime: { - sendMessage(request: TRequest, response: (response: TResponse) => void); + sendMessage(request: TRequest, response: (response: TResponse) => void): void; onMessage: { - addListener(listener: (message, any, sendResponse) => void) + addListener(listener: (message: TRequest, sender: MessageSender, sendResponse: (response: TResponse) => void) => void): void } } @@ -11,13 +11,13 @@ declare type Browser = { local: BrowserStorage } tabs: { - remove(id: number) + remove(id: number): void sendMessage(tabId: number, request: TRequest, options?: { frameId: number - }, callback?: (response: TResponse) => void) + }, callback?: (response: TResponse) => void): void create(options: { url: string - }) + }): void } extension: { getURL(relativeUrl: string): string @@ -36,13 +36,20 @@ declare type Browser = { linkUrl: string }, tab: { id: number - }) => void) + }) => void): void } } } +type MessageSender = { + tab?: {id: number}; + frameId?: number; + id?: string; + url?: string; + tlsChannelId?: string; + }; declare type BrowserStorage = { - get(names: string[], callback: (obj: any) => void) - set(obj: { [name: string]: any }); + get(names: string[], callback: (obj: any) => void): void + set(obj: { [name: string]: any }): void; } declare var browser: Browser; declare var chrome: Browser; \ No newline at end of file