Fix more type errors, refactor a few things
This commit is contained in:
parent
db717cfe15
commit
da80996a8c
@ -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] = <any>[];
|
||||
overrides[MIGRATION] = <any>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<ShinigamiEyesMessage, ShinigamiEyesMessage | LabelMap>((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] = <any>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 <any>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] = <any>Object.getOwnPropertyNames(overrides)
|
||||
.map<ShinigamiEyesSubmission>(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 = <LabelKind>info.menuItemId.substring('mark-'.length);
|
||||
if (label == 'none') label = '';
|
||||
browser.tabs.sendMessage<ShinigamiSubmission, ShinigamiSubmission>(tabId, {
|
||||
browser.tabs.sendMessage<ShinigamiEyesSubmission, ShinigamiEyesSubmission>(tabId, {
|
||||
mark: label,
|
||||
url: info.linkUrl,
|
||||
tabId: tabId,
|
||||
frameId: frameId,
|
||||
// elementId: info.targetElementId,
|
||||
debug: overrides.debug
|
||||
debug: <any>overrides.debug
|
||||
}, { frameId: frameId }, response => {
|
||||
if (!response.identifier) return;
|
||||
if (response.mark){
|
||||
|
@ -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(<HTMLAnchorElement>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(<HTMLAnchorElement>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(<any>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 = <HTMLElement>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 = <HTMLAnchorElement>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<ShinigamiEyesCommand, LabelMap>({ ids: uniqueIdentifiers, myself: <string>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 && (<HTMLElement>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 = <HTMLElement>urlstr;
|
||||
let p = <HTMLElement>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<ShinigamiEyesMessage, ShinigamiEyesSubmission>((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((<HTMLAnchorElement>target).href) break;
|
||||
target = target.parentElement;
|
||||
}
|
||||
|
||||
if (target && target.href != message.url) target = null;
|
||||
if (target && (<HTMLAnchorElement>target).href != message.url) target = null;
|
||||
|
||||
var identifier = target ? getIdentifier(target) : getIdentifier(message.url);
|
||||
var identifier = target ? getIdentifier(<HTMLAnchorElement>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';
|
||||
|
37
extension/definitions.d.ts
vendored
37
extension/definitions.d.ts
vendored
@ -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';
|
||||
|
@ -1,5 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2017"
|
||||
"target": "es2017",
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
}
|
||||
}
|
23
extension/webextensions.d.ts
vendored
23
extension/webextensions.d.ts
vendored
@ -1,9 +1,9 @@
|
||||
declare type Browser = {
|
||||
|
||||
runtime: {
|
||||
sendMessage<TRequest, TResponse>(request: TRequest, response: (response: TResponse) => void);
|
||||
sendMessage<TRequest, TResponse>(request: TRequest, response: (response: TResponse) => void): void;
|
||||
onMessage: {
|
||||
addListener(listener: (message, any, sendResponse) => void)
|
||||
addListener<TRequest, TResponse>(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<TRequest, TResponse>(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;
|
Loading…
Reference in New Issue
Block a user