Skip to content
This repository has been archived by the owner on Nov 21, 2022. It is now read-only.

Commit

Permalink
Merge pull request #79 from ribicn-softerrific/#66_Alpha_v1
Browse files Browse the repository at this point in the history
0.1alpha initial commit
  • Loading branch information
malteserteresa authored Jan 22, 2020
2 parents 79bbe78 + 5f8855f commit a4cae17
Show file tree
Hide file tree
Showing 22 changed files with 1,568 additions and 280 deletions.
21 changes: 17 additions & 4 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,28 @@ env:
browser: true
es6: true
webextensions: true
jest: true
node: true
extends:
- airbnb-base
- standard
globals:
Atomics: readonly
SharedArrayBuffer: readonly
browser: readonly
parserOptions:
ecmaVersion: 2018
sourceType: module
rules: {}

rules:
no-console: off
indent:
- error
- 2
- {SwitchCase: 1}
linebreak-style:
- error
- unix
quotes:
- error
- single
semi:
- error
- always
201 changes: 151 additions & 50 deletions extension/content/opt-out-ext.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
let selector;
let option = 'text_crossed';
let slider = '1';

function updateOption(result) {
option = result.optOut.selector;
slider = result.optOut.slider;
}

function onError(error) {
console.log(`Error: ${error}`);
}
browser.storage.sync.get('optOut').then(updateOption, onError);

const bodyColor = window.getComputedStyle(document.body, null).getPropertyValue('background-color');
document.documentElement.style
.setProperty('--color', bodyColor);

const root = document.getElementById('doc') || document.getElementById('react-root');

Expand All @@ -22,87 +14,160 @@ if (document.querySelector('body').classList.contains('logged-out')) {
console.log('online');
selector = '[data-testid="tweet"]';
}
/**
* @description Updates `option` and `slider` depending on the given result
* @param result
*/
const updateOption = (result) => {
option = result.optOut.selector;
slider = result.optOut.slider;
};

/**
* @description Function handles errors.
* @param error
*/
const onError = (error) => {
console.error(`Error: ${error}`);
};

/**
* @description Depending on `option` sets classes to tweet nodes
*/
const styleTweet = (element, selectedOption, sliderValue) => {
element.classList.remove('opt-out-tw', 'opt-out-tc', 'opt-out-trem');
if (sliderValue === '1') {
switch (selectedOption) {
case 'text_white':
element.classList.add('opt-out-tw');
break;
case 'text_crossed':
element.classList.add('opt-out-tc');
break;
case 'text_removed':
element.classList.add('opt-out-trem');
break;
}
}
element.nextElementSibling.style.display = (sliderValue === '1') ? 'flex' : 'none';
};

/*
Depending on `option` sets classes to tweet nodes
/**
* @description Should inject check Buttons to post
* @param node
*/
const styleTweet = function (element, selectedOption, sliderValue) {
if ((selectedOption === 'text_white') && (sliderValue === '1')) element.classList.add('opt-out-tw');
else element.classList.remove('opt-out-tw');
if ((selectedOption === 'text_crossed') && (sliderValue === '1')) element.classList.add('opt-out-tc');
else element.classList.remove('opt-out-tc');
if ((selectedOption === 'text_removed') && (sliderValue === '1')) element.classList.add('opt-out-trem');
else element.classList.remove('opt-out-trem');
const injectReportMisogynisticButtons = (node) => {
const buttons = document.createElement('div');
buttons.classList.add('report-button-container');
const button1 = document.createElement('button');
button1.appendChild(document.createTextNode('Is misogynistic'));
button1.classList.add('report-button', 'report-button_is');
const button2 = document.createElement('button');
button2.appendChild(document.createTextNode('Isn\'t misogynistic'));
button2.classList.add('report-button', 'report-button_isnt');
buttons.appendChild(button1);
buttons.appendChild(button2);
node.parentNode.insertBefore(buttons, node.nextSibling);
};

/*
function which calls server for given node, and depending on the response,
applies pre-defined action
/**
* @description function which calls server for given node, and depending on the response,
* applies pre-defined action
* @param node
*/
const checkText = function (node) {
const checkText = (node) => {
node.classList.add('processing');
console.log('Sending Request');
const link = 'https://api.optoutools.com/predict';
const xhr = new XMLHttpRequest();
const tweetTextNode = node.querySelector(
`${selector} > div ~ div > div ~ div`
);
xhr.open('POST', link, true);
xhr.setRequestHeader('Content-type', 'application/json;charset=UTF-8');
xhr.withCredentials = true;
xhr.onreadystatechange = function (e) {
xhr.onreadystatechange = (e) => {
if (xhr.readyState !== 4) {
return;
}
if (xhr.status === 200) {
console.log(
'Response received as ',
JSON.parse(xhr.response).predictions[0],
JSON.parse(xhr.response).predictions[0]
);
injectReportMisogynisticButtons(tweetTextNode);
if (JSON.parse(xhr.response).predictions[0]) {
node.classList.add('processed-true');
const tweetText = node.querySelector(
`${selector} > div ~ div > div ~ div`,
);
styleTweet(tweetText, option, slider);
styleTweet(tweetTextNode, option, slider);
} else {
node.classList.add('processed-false');
}
} else {
console.error(e);
// console.error(e);
console.log('Failed response', xhr);
}
};
xhr.send(
JSON.stringify({
texts: [node.innerText],
}),
texts: [tweetTextNode.innerText]
})
);
};

/*
* Predefines action and changes it depending on user action
/**
* @description function which sends tweet text to
* @param tweetNode
* @param isMisogynistic
*/
browser.runtime.onMessage.addListener((message) => {
if ((option !== message.selector) || (slider !== message.slider)) {
option = message.selector;
slider = message.slider;
const posts = document.querySelectorAll('.processed-true');
posts.forEach((post) => {
const tweetText = post.querySelector(
`${selector} > div ~ div > div ~ div`,
); // selecting text inside tweet
styleTweet(tweetText, option, slider);
});
}
});
const reportMisogyny = (tweetNode, isMisogynistic) => {
const tweetText = tweetNode.querySelector(
`${selector} > div ~ div > div ~ div`
).innerText;
const tweetId = tweetNode.querySelector(
`${selector} > div ~ div > div > div > a`
).href.split('/').filter(e => e).slice(-1)[0];
const link = 'https://api.optoutools.com/mislabeled_tweet';
const xhr = new XMLHttpRequest();
xhr.open('POST', link, true);
xhr.setRequestHeader('Content-type', 'application/json;charset=UTF-8');
xhr.withCredentials = true;
xhr.onreadystatechange = (e) => {
if (xhr.readyState !== 4) {
return;
}
if (xhr.status === 200) {
// What to do when tweet is reported, disable buttons? replace with message?
} else {
console.log('Failed response', xhr);
}
};
xhr.send(
JSON.stringify({
text: tweetText,
id: tweetId,
model_version: isMisogynistic
})
);
};

const processTweets = function () {
/**
* @description get every tweet and process unprocessed ones.
*/
const processTweets = () => {
const posts = document.querySelectorAll(selector); // selecting tweet object
posts.forEach((post) => {
if (post.classList.contains('processed-true')) return;
if (post.classList.contains('processed-false')) return;
if (post.classList.contains('processing')) return;
checkText(post);
});
};

const checkTweetList = function (mutationsList) {
/**
* @description for every change in DOM run processTweets
* @param mutationsList
*/
const checkTweetList = (mutationsList) => {
mutationsList.forEach((mutation) => {
if (mutation.type === 'childList') {
processTweets();
Expand All @@ -111,4 +176,40 @@ const checkTweetList = function (mutationsList) {
};

const checkTweetListObserver = new MutationObserver(checkTweetList);

// MAIN FUNCTION

browser.storage.sync.get('optOut').then(updateOption, onError);
/**
* Adds listener which on new message received from popup goes over tweets and applies new style
*/
browser.runtime.onMessage.addListener((message) => {
if ((option !== message.selector) || (slider !== message.slider)) {
option = message.selector;
slider = message.slider;
const posts = document.querySelectorAll('.processed-true');
posts.forEach((post) => {
const tweetText = post.querySelector(
`${selector} > div ~ div > div ~ div`
); // selecting text inside tweet
styleTweet(tweetText, option, slider);
});
}
});

/**
* Adds listener which listens for click from 'report_button' and triggers corresponding action
*/
document.addEventListener('click', function (event) {
if (!event.target.matches('.report-button')) return;
event.preventDefault();
const tweetNode = event.target.parentNode.parentNode.parentNode;
const isMisogynistic = (event.target.matches('.report-button_is')) ? 1 : 0;
reportMisogyny(tweetNode, isMisogynistic);
for (const button of event.target.parentNode.children) {
button.style.cursor = 'not-allowed';
button.disabled = true;
}
}, false);

checkTweetListObserver.observe(root, { childList: true, subtree: true });
30 changes: 29 additions & 1 deletion extension/content/opt-out.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
:root {
--color: rgb(255, 255, 255);
}
.opt-out-tw {
color: white;
color: var(--color, white);
}
.opt-out-tw a {
color: var(--color, white);
}

.opt-out-tc {
Expand All @@ -9,3 +15,25 @@
.opt-out-trem {
display: none;
}

.report-button-container {
display: flex;
justify-content: space-around;
}

.report-button {
width: 180px;
height: 40px;
padding-left: 37px;
border-radius: 5px;
}

.report-button_is {
background: 5px center no-repeat url(../popup/assets/angry.svg);
background-size: auto 80%;
}

.report-button_isnt {
background: 5px center no-repeat url(../popup/assets/happy.svg);
background-size: auto 80%;
}
5 changes: 3 additions & 2 deletions extension/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"webRequestBlocking",
"activeTab",
"storage",
"https://api.optoutools.com/predict"
"https://api.optoutools.com/predict",
"https://api.optoutools.com/mislabeled_tweet"
],
"applications": {
"gecko": {
Expand All @@ -20,7 +21,7 @@
"browser_action": {
"default_icon": "icons/icon-48.png",
"default_title": "Opt-Out-Ext",
"default_popup": "popup/set_view_mod.html"
"default_popup": "popup/popup.html"
},
"icons": {
"48": "icons/icon-48.png",
Expand Down
File renamed without changes
File renamed without changes
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ h2 {

#slider {
width: 100%;
background: #d3d3d3;
background: #d3d3d3;
height: 10px;
border-radius: 5px;
margin-bottom: 24px;
}

#slider::-moz-range-thumb {
width: 50px;
width: 50px;
height: 50px;
cursor: pointer;
border: none;
Expand All @@ -37,7 +37,7 @@ h2 {
}

#slider::-webkit-slider-thumb {
width: 50px;
width: 50px;
height: 50px;
cursor: pointer;
border: none;
Expand Down
10 changes: 10 additions & 0 deletions extension/popup/functions/onError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* @description Display the popup's error message, and hide the normal UI.
*
* @param error - contains error message
*/
export default function (error) {
document.querySelector('#popup-content').classList.add('hidden');
document.querySelector('#error-content').classList.remove('hidden');
console.error(`Failed to execute opt-out content script: ${error.message}`);
}
Loading

0 comments on commit a4cae17

Please sign in to comment.