First Commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
node_modules
|
||||||
48
extension/background.js
Normal file
48
extension/background.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
const activeTabs = {};
|
||||||
|
let contentScriptRerunner = null;
|
||||||
|
|
||||||
|
function runContentScript(tabId) {
|
||||||
|
chrome.tabs.executeScript(tabId, {
|
||||||
|
allFrames: true,
|
||||||
|
file: 'run.js',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function rerunContentScripts() {
|
||||||
|
// unfortunately we need to rerun periodically to handle iframes changing..
|
||||||
|
// eventually fixed when we require the new host permission or chrome releases a content script api
|
||||||
|
for (const tabId of Object.keys(activeTabs)) {
|
||||||
|
runContentScript(parseInt(tabId, 10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerDynamicContentScript() {
|
||||||
|
chrome.tabs.onUpdated.addListener((tabId, {status}, {url}) => {
|
||||||
|
if (!status || !url) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
runContentScript(tabId);
|
||||||
|
activeTabs[tabId] = true;
|
||||||
|
|
||||||
|
if (contentScriptRerunner == null) {
|
||||||
|
contentScriptRerunner = setInterval(rerunContentScripts, 5000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
chrome.tabs.onRemoved.addListener((tabId) => {
|
||||||
|
delete activeTabs[tabId];
|
||||||
|
|
||||||
|
if (contentScriptRerunner != null && Object.keys(activeTabs).length === 0) {
|
||||||
|
clearInterval(contentScriptRerunner);
|
||||||
|
contentScriptRerunner = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
chrome.runtime.onInstalled.addListener(({reason}) => {
|
||||||
|
if (reason !== 'install') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
registerDynamicContentScript();
|
||||||
BIN
extension/icon.png
Normal file
BIN
extension/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
34
extension/manifest.json
Normal file
34
extension/manifest.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"background": {
|
||||||
|
"scripts": ["background.js"]
|
||||||
|
},
|
||||||
|
"browser_action": {
|
||||||
|
"default_popup": "settings.html"
|
||||||
|
},
|
||||||
|
"content_scripts": [
|
||||||
|
{
|
||||||
|
"all_frames": true,
|
||||||
|
"exclude_matches": [
|
||||||
|
"*://*.twitch.tv/*.html",
|
||||||
|
"*://*.twitch.tv/*.html?*",
|
||||||
|
"*://*.twitch.tv/*.htm",
|
||||||
|
"*://*.twitch.tv/*.htm?*"
|
||||||
|
],
|
||||||
|
"js": ["run.js"],
|
||||||
|
"matches": ["*://*.twitch.tv/*"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Add notes to twitch users",
|
||||||
|
"externally_connectable": {
|
||||||
|
"matches": ["*://*.twitch.tv/*"]
|
||||||
|
},
|
||||||
|
"homepage_url": "https://rdarius.lt/projects/chrome-extensions/twitch-notes",
|
||||||
|
"icons": {
|
||||||
|
"128": "icon.png"
|
||||||
|
},
|
||||||
|
"manifest_version": 2,
|
||||||
|
"name": "Twitch Notes",
|
||||||
|
"permissions": ["*://*.twitch.tv/*"],
|
||||||
|
"version": "0.0.1",
|
||||||
|
"web_accessible_resources": ["twitchnotes.js", "twitchnotes.css"]
|
||||||
|
}
|
||||||
46
extension/notes-notepad-svgrepo-com.svg
Normal file
46
extension/notes-notepad-svgrepo-com.svg
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 508.1 508.1" style="enable-background:new 0 0 508.1 508.1;" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M440.85,55.3h-168.3v-0.7c0-30.1-24.5-54.6-54.6-54.6c-30.1,0-54.6,24.5-54.6,54.6c0,0.2,0.1,0.5,0.1,0.7h-31.7
|
||||||
|
c-7.8,0-14.1,6.3-14.1,14.1v43.4h-50.4c-7.8,0-14.1,6.3-14.1,14.1v262.4c0,3.7,1.5,7.3,4.1,10L161.85,504c2.6,2.6,6.2,4.1,10,4.1
|
||||||
|
h204.5c7.8,0,14.1-6.3,14.1-14.1v-37.5h50.4c7.8,0,14.1-6.3,14.1-14.1v-373C454.95,61.6,448.65,55.3,440.85,55.3z M191.55,54.6
|
||||||
|
c0-14.5,11.8-26.4,26.4-26.4c14.6,0,26.4,11.8,26.4,26.4v0.7h-52.8C191.55,55.1,191.55,54.8,191.55,54.6z M145.85,83.5h98.5v29.3
|
||||||
|
h-98.5V83.5z M244.35,141v31c0,14.5-11.8,26.4-26.4,26.4s-26.4-11.8-26.4-26.4v-31H244.35z M157.75,459.8l-56.5-56.5h56.5V459.8z
|
||||||
|
M362.25,479.8h-176.2v-90.5c0-7.8-6.3-14.1-14.1-14.1h-90.6V141h82v31c0,30.1,24.5,54.6,54.6,54.6c30.1,0,54.6-24.5,54.6-54.6
|
||||||
|
v-31h89.7V479.8z M426.75,428.1h-36.3V126.9c0-7.8-6.3-14.1-14.1-14.1h-103.8V83.5h154.2V428.1z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
110
extension/run.js
Normal file
110
extension/run.js
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
(function twitchnotes() {
|
||||||
|
if (document.querySelector("script#twitchnotes")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const script = document.createElement("script");
|
||||||
|
script.type = "text/javascript";
|
||||||
|
script.src = chrome.runtime.getURL("twitchnotes.js");
|
||||||
|
script.id = "twitchnotes";
|
||||||
|
const head = document.getElementsByTagName("head")[0];
|
||||||
|
if (!head) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
head.appendChild(script);
|
||||||
|
|
||||||
|
const style = document.createElement("style");
|
||||||
|
style.innerHTML = `
|
||||||
|
:root {
|
||||||
|
--window-close: #f77669;
|
||||||
|
--window-minify: #ffcb6b;
|
||||||
|
--window-expand: #c3e88d;
|
||||||
|
|
||||||
|
--background-header: #393d3e;
|
||||||
|
--background-code: #1f1e1e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.twitchnote img {
|
||||||
|
height: 18px;
|
||||||
|
padding: 0 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.twitch-note-container {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1000000;
|
||||||
|
min-width: 420px;
|
||||||
|
max-width: 95vw;
|
||||||
|
min-height: 240px;
|
||||||
|
max-height: 50vh;
|
||||||
|
background: var(--color-background-base);
|
||||||
|
border: var(--border-width-default) solid var(--color-border-base) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.twitch-note-header {
|
||||||
|
width: 100%;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
border-bottom: var(--border-width-default) solid var(--color-border-base) !important;
|
||||||
|
position: relative;
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
|
||||||
|
.twitch-note-title {
|
||||||
|
color: var(--color-text-alt) !important;
|
||||||
|
font-size: var(--font-size-6) !important;
|
||||||
|
font-weight: var(--font-weight-semibold) !important;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
padding-left: calc((32px - var(--font-size-6)) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.twitch-note-close-button {
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
top: 5px;
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
line-height: 22px;
|
||||||
|
text-align: center;
|
||||||
|
aspect-ration: 1;
|
||||||
|
color: var(--color-text-alt) !important;
|
||||||
|
font-size: var(--font-size-6) !important;
|
||||||
|
font-weight: var(--font-weight-semibold) !important;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
border: var(--border-width-default) solid var(--color-border-base) !important;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.twitch-note-content {
|
||||||
|
position: absolute;
|
||||||
|
top: 32px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 32px;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-text-base) !important;
|
||||||
|
font-family: var(--font-base);
|
||||||
|
vertical-align: baseline;
|
||||||
|
outline: none;
|
||||||
|
overflow: auto;
|
||||||
|
padding: calc((32px - var(--font-size-6)) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.twitch-note-save-button {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 32px;
|
||||||
|
border-top: var(--border-width-default) solid var(--color-border-base) !important;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--color-text-alt) !important;
|
||||||
|
font-size: var(--font-size-6) !important;
|
||||||
|
font-weight: var(--font-weight-semibold) !important;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
head.appendChild(style);
|
||||||
|
})();
|
||||||
3
extension/settings.html
Normal file
3
extension/settings.html
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<div style="width: 250px">
|
||||||
|
Exporting, Importing and Data clearing will be available in the future
|
||||||
|
</div>
|
||||||
200
extension/twitchnotes.js
Normal file
200
extension/twitchnotes.js
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
let mousePosition = { x: 0, y: 0 };
|
||||||
|
let lastMousePosition = { x: 0, y: 0 };
|
||||||
|
let openContainers = {};
|
||||||
|
let isMouseDown = false;
|
||||||
|
let activeContainer = null;
|
||||||
|
|
||||||
|
document.addEventListener("mousemove", handleMouseMove);
|
||||||
|
document.addEventListener("mouseup", handleMouseUp);
|
||||||
|
|
||||||
|
function saveNote(username, note) {
|
||||||
|
let userList = JSON.parse(
|
||||||
|
localStorage.getItem("twitch-note-all-users-list") || "[]"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!userList.includes(username)) {
|
||||||
|
userList.push(username);
|
||||||
|
localStorage.setItem(
|
||||||
|
"twitch-note-all-users-list",
|
||||||
|
JSON.stringify(userList)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem("twitch-note-" + username, note);
|
||||||
|
}
|
||||||
|
function getNote(username) {
|
||||||
|
return localStorage.getItem("twitch-note-" + username) || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMouseMove(event) {
|
||||||
|
event = event || window.event;
|
||||||
|
if (isMouseDown) {
|
||||||
|
if (activeContainer) {
|
||||||
|
const dx = mousePosition.x - lastMousePosition.x;
|
||||||
|
const dy = mousePosition.y - lastMousePosition.y;
|
||||||
|
openContainers[activeContainer].style.top =
|
||||||
|
parseInt(openContainers[activeContainer].style.top) + dy + "px";
|
||||||
|
openContainers[activeContainer].style.left =
|
||||||
|
parseInt(openContainers[activeContainer].style.left) + dx + "px";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateMousePosition(event.clientX, event.clientY);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMouseUp(event) {
|
||||||
|
activeContainer = null;
|
||||||
|
isMouseDown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateMousePosition(x, y) {
|
||||||
|
lastMousePosition = { ...mousePosition };
|
||||||
|
mousePosition.x = x;
|
||||||
|
mousePosition.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeContainer(username) {
|
||||||
|
if (!openContainers[username]) return;
|
||||||
|
openContainers[username].remove();
|
||||||
|
delete openContainers[username];
|
||||||
|
}
|
||||||
|
|
||||||
|
function addContainer(username, container) {
|
||||||
|
if (openContainers[username]) return;
|
||||||
|
openContainers[username] = container;
|
||||||
|
document.body.appendChild(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openTwitchNote(username) {
|
||||||
|
// CONTAINER
|
||||||
|
const container = document.createElement("div");
|
||||||
|
container.style.left = mousePosition.x + "px";
|
||||||
|
container.style.top = mousePosition.y + 10 + "px";
|
||||||
|
container.classList.add("twitch-note-container");
|
||||||
|
|
||||||
|
// HEADER
|
||||||
|
const header = document.createElement("div");
|
||||||
|
header.classList.add("twitch-note-header");
|
||||||
|
header.addEventListener("mousedown", function close() {
|
||||||
|
activeContainer = username;
|
||||||
|
isMouseDown = true;
|
||||||
|
});
|
||||||
|
const closeButton = document.createElement("span");
|
||||||
|
closeButton.classList.add("twitch-note-close-button");
|
||||||
|
closeButton.innerText = "X";
|
||||||
|
closeButton.addEventListener("click", function close() {
|
||||||
|
removeContainer(username);
|
||||||
|
});
|
||||||
|
header.appendChild(closeButton);
|
||||||
|
const title = document.createElement("span");
|
||||||
|
title.classList.add("twitch-note-title");
|
||||||
|
title.innerHTML = username;
|
||||||
|
header.appendChild(title);
|
||||||
|
container.appendChild(header);
|
||||||
|
|
||||||
|
// BODY
|
||||||
|
const content = document.createElement("div");
|
||||||
|
content.classList.add("twitch-note-content");
|
||||||
|
content.setAttribute("contentEditable", true);
|
||||||
|
content.innerHTML = getNote(username);
|
||||||
|
|
||||||
|
container.appendChild(content);
|
||||||
|
|
||||||
|
const saveButton = document.createElement("div");
|
||||||
|
saveButton.classList.add("twitch-note-save-button");
|
||||||
|
saveButton.innerText = "SAVE";
|
||||||
|
saveButton.addEventListener("click", function () {
|
||||||
|
saveNote(username, content.innerHTML);
|
||||||
|
removeContainer(username);
|
||||||
|
});
|
||||||
|
|
||||||
|
container.appendChild(saveButton);
|
||||||
|
|
||||||
|
addContainer(username, container);
|
||||||
|
}
|
||||||
|
|
||||||
|
(function runTwitchNotes() {
|
||||||
|
// Select the node that will be observed for mutations
|
||||||
|
|
||||||
|
let giveUpAt = Date.now() + 20000;
|
||||||
|
|
||||||
|
let changeInProgress = false;
|
||||||
|
|
||||||
|
let targetNode;
|
||||||
|
let buttonContainer;
|
||||||
|
|
||||||
|
do {
|
||||||
|
targetNode = document.getElementsByClassName(
|
||||||
|
"chat-scrollable-area__message-container"
|
||||||
|
)[0];
|
||||||
|
} while (!targetNode || giveUpAt < Date.now());
|
||||||
|
|
||||||
|
do {
|
||||||
|
buttonContainer = document.getElementsByClassName(
|
||||||
|
"chat-input__buttons-container"
|
||||||
|
)[0];
|
||||||
|
} while (!buttonContainer || giveUpAt < Date.now());
|
||||||
|
|
||||||
|
const settingsButton = document.createElement("div");
|
||||||
|
settingsButton.innerHTML = "<b>Notes</b>";
|
||||||
|
|
||||||
|
buttonContainer.lastChild.insertBefore(
|
||||||
|
settingsButton,
|
||||||
|
buttonContainer.lastChild.firstChild
|
||||||
|
);
|
||||||
|
|
||||||
|
// Options for the observer (which mutations to observe)
|
||||||
|
const config = { attributes: true, childList: true, subtree: true };
|
||||||
|
|
||||||
|
// Callback function to execute when mutations are observed
|
||||||
|
const callback = (mutationList, observer) => {
|
||||||
|
if (!changeInProgress) {
|
||||||
|
for (const mutation of mutationList) {
|
||||||
|
if (mutation.type === "childList") {
|
||||||
|
changeInProgress = true;
|
||||||
|
let nodes = document.getElementsByClassName("chat-line__message");
|
||||||
|
let users = [];
|
||||||
|
for (let node of nodes) {
|
||||||
|
const n = node.querySelector(".chat-author__display-name");
|
||||||
|
if (!n) continue;
|
||||||
|
const usernameContainer = node.querySelector(
|
||||||
|
".chat-line__username-container"
|
||||||
|
);
|
||||||
|
|
||||||
|
const username = n.attributes["data-a-user"].value;
|
||||||
|
if (username) {
|
||||||
|
let noteButton = usernameContainer.querySelector(".twitchnote");
|
||||||
|
if (!noteButton) {
|
||||||
|
const twitchNote = document.createElement("span");
|
||||||
|
twitchNote.classList.add("twitchnote");
|
||||||
|
twitchNote.style.cursor = "pointer";
|
||||||
|
twitchNote.addEventListener("click", () => {
|
||||||
|
openTwitchNote(username);
|
||||||
|
});
|
||||||
|
|
||||||
|
const img = document.createElement("img");
|
||||||
|
img.src = "https://cdn.rdarius.lt/icons/32-id-card.png";
|
||||||
|
twitchNote.appendChild(img);
|
||||||
|
|
||||||
|
usernameContainer.insertBefore(
|
||||||
|
twitchNote,
|
||||||
|
usernameContainer.firstChild
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!users[username]) {
|
||||||
|
users[username] = 0;
|
||||||
|
}
|
||||||
|
users[username]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
changeInProgress = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create an observer instance linked to the callback function
|
||||||
|
const observer = new MutationObserver(callback);
|
||||||
|
|
||||||
|
// Start observing the target node for configured mutations
|
||||||
|
observer.observe(targetNode, config);
|
||||||
|
})();
|
||||||
Reference in New Issue
Block a user