diff --git a/extension/twitch-notes.bak.js b/extension/twitch-notes.bak.js
deleted file mode 100644
index 38563b7..0000000
--- a/extension/twitch-notes.bak.js
+++ /dev/null
@@ -1,691 +0,0 @@
-(function runTwitchNotes() {
- function save(filename, data) {
- const blob = new Blob([data], {type: 'text/json'});
- const elem = window.document.createElement('a');
- elem.href = window.URL.createObjectURL(blob);
- elem.download = filename;
- document.body.appendChild(elem);
- elem.click();
- document.body.removeChild(elem);
- }
-
- const Notes = {
- inputData: {},
- getSavedUserList: () => {
- return JSON.parse(localStorage.getItem(LS_UserList) || "[]");
- },
- addUserToSavedList: (username) => {
- let userList = Notes.getSavedUserList();
-
- if (!userList.includes(username)) {
- userList.push(username);
- localStorage.setItem(LS_UserList, JSON.stringify(userList));
- }
- },
- removeUserFromSavedList: (username) => {
- let userList = Notes.getSavedUserList();
- userList = userList.filter(u => u !== username);
- if (userList.length) {
- localStorage.setItem(LS_UserList, JSON.stringify(userList));
- } else {
- localStorage.removeItem(LS_UserList);
- }
- },
- saveNote: (username, note) => {
- Notes.addUserToSavedList(username);
- localStorage.setItem(LS_Prefix + username, note);
- },
- deleteNote: (username) => {
- Notes.removeUserFromSavedList(username);
- localStorage.removeItem(LS_Prefix + username);
- },
- getNote: (username) => {
- return localStorage.getItem(LS_Prefix + username) || "";
- },
- exportNotes: () => {
- save('twitch-notes-' + (Date.now()) + '.json', JSON.stringify({
- users: Notes.getSavedUserList(),
- notes: Notes.getSavedUserList().map(user => {
- return {user: user, note: Notes.getNote(user)};
- }),
- settings: {},
- }));
- },
- showAllNotes: () => {
- let activeNote = null;
- const blurredBackground = createElement('div', null, 'twitch-notes-blurred-background');
- const container = createElement('div', null, 'twitch-notes-center-floating-container', {
- padding: '0',
- paddingTop: '1rem',
- width: '100%',
- height: 'calc(320px + 5rem)'
- });
- const closeButton = createElement('button', xButton, 'twitch-notes-settings-close-button', {
- position: 'absolute',
- top: '1rem',
- right: '1rem',
- });
-
- const title = createElement('div', 'Notes:', 'twitch-note-title', {
- borderBottom: '1px solid #FFFFFF19',
- paddingBottom: '1rem',
- });
- container.appendChild(title);
-
- const content = createElement('div', null, null, {
- display: 'flex',
- flexDirection: 'row',
- flexWrap: 'no-wrap',
- width: '100%',
- height: '320px'
- });
- container.appendChild(content);
- closeButton.addEventListener('click', () => {
- blurredBackground.remove();
- });
- container.appendChild(closeButton);
-
- const userList = createElement('div', null, null, {
- borderRight: '1px solid #FFFFFF19',
- height: '320px',
- minWidth: '240px',
- overflowY: 'auto',
- overflowX: 'hidden',
- });
-
- const noteContainer = createElement('div', null, null, {
- height: '320px',
- padding: '1rem',
- width: '100%',
- opacity: '0',
- });
-
- const note = createElement('div', null, null, {
- marginBottom: '3rem',
- border: '1px solid #FFFFFF19',
- height: 'calc(320px - 7rem)',
- width: '100%',
- padding: '6px',
- outline: 'none',
- overflowY: 'auto',
- overflowX: 'hidden',
- }, {
- contentEditable: 'true',
- });
- const saveNote = createElement('button', 'Save Note', 'twitch-notes-settings-button', {
- position: 'absolute',
- bottom: '1rem',
- left: '1rem'
- });
-
- saveNote.addEventListener('click', () => {
- if(!activeNote) {
- return;
- }
- Notes.saveNote(activeNote, note.innerHTML);
- })
-
- for(let user of Notes.getSavedUserList()) {
- const userLine = createElement('div', user, 'twitch-notes-user-list-user');
- userLine.addEventListener('click', () => {
- const list = document.getElementsByClassName('twitch-notes-user-list-user');
- for(let i = 0; i < list.length; i++) {
- list.item(i).removeAttribute('data-active');
- }
- userLine.setAttribute('data-active', 'true');
- noteContainer.style.opacity = '1';
- note.innerHTML = Notes.getNote(user);
- activeNote = user;
- });
- userList.appendChild(userLine);
- }
-
- noteContainer.appendChild(note);
- noteContainer.appendChild(saveNote);
-
- content.appendChild(userList);
- content.appendChild(noteContainer);
-
- blurredBackground.appendChild(container);
- document.body.appendChild(blurredBackground);
- },
- importNotesConflictResolve: (user, resultsContainer, container, blurredBackground) => {
- const diffBlurredBackground = createElement('div', null, 'twitch-notes-blurred-background');
- const diffContainer = createElement('div', 'Select which note to keep for ' + user + ' You can modify notes to merge them and keep updated one ', 'twitch-notes-center-floating-container');
-
- const inputContainer = createElement('div', null, null, {
- display: 'flex',
- flexDirection: 'row',
- flexWrap: 'no-wrap',
- width: '100%',
- });
-
- const valueStyle = {
- minWidth: '320px',
- padding: '6px',
- border: '1px solid #FFFFFF19',
- outline: 'none',
- };
- const valueAttr = {contentEditable: 'true'};
-
- const localValueContainer = createElement('div');
- const pLocal = createElement('p', 'Locally saved note ');
- localValueContainer.appendChild(pLocal);
- const localValue = createElement('div', Notes.getNote(user), null, valueStyle, valueAttr, 'local-value-container');
- const localValueSave = createElement('button', 'Save this', 'twitch-notes-settings-button');
- localValueSave.addEventListener('click', () => {
- const val = document.getElementById('local-value-container').innerHTML;
- Notes.inputData.notes = Notes.inputData.notes.map(n => n.user === user ? {
- user: n.user,
- note: val,
- resolved: true
- } : n);
- diffBlurredBackground.remove();
- resultsContainer.remove();
- Notes.importNotesResult(container, blurredBackground);
- });
- localValueContainer.appendChild(localValue);
- localValueContainer.innerHTML += ' ';
- localValueContainer.appendChild(localValueSave);
-
- const importedValueContainer = createElement('div');
- const iLocal = createElement('p', 'Imported note ');
- importedValueContainer.appendChild(iLocal);
- const importedValue = createElement('div', Notes.inputData.notes.filter(n => n.user === user)[0].note || '', null, valueStyle, valueAttr, 'import-value-container');
- const importedValueSave = createElement('button', 'Save this', 'twitch-notes-settings-button');
- importedValueSave.addEventListener('click', () => {
- const val = document.getElementById('import-value-container').innerHTML;
- Notes.inputData.notes = Notes.inputData.notes.map(n => n.user === user ? {
- user: n.user,
- note: val,
- resolved: true
- } : n);
- diffBlurredBackground.remove();
- resultsContainer.remove();
- Notes.importNotesResult(container, blurredBackground);
- });
-
- importedValueContainer.appendChild(importedValue);
- importedValueContainer.innerHTML += ' ';
- importedValueContainer.appendChild(importedValueSave);
-
-
- inputContainer.appendChild(localValueContainer);
- inputContainer.appendChild(importedValueContainer);
-
- diffContainer.appendChild(inputContainer);
- diffBlurredBackground.appendChild(diffContainer);
- document.body.appendChild(diffBlurredBackground);
- },
- importNotesResult: (container, blurredBackground) => {
-
- const willBeAdded = [];
- let willBeOverwritten = [];
- const noChanges = [];
-
- for (let user of Notes.inputData.users) {
- if (Notes.getNote(user)) {
- if (Notes.inputData.notes.find(note => note.user === user).resolved || Notes.getNote(user) !== Notes.inputData.notes.find(note => note.user === user).note) {
- willBeOverwritten.push(user);
- } else {
- noChanges.push(user);
- }
- } else {
- willBeAdded.push(user);
- }
- }
-
- const resultsContainer = createElement('div', null);
-
- if (willBeOverwritten.length) {
- const overwrittenContainer = createElement('div', 'Note conflicts found for: ', null, {
- marginTop: '1rem',
- });
- for (let item of willBeOverwritten) {
- const row = createElement('div', item + ' ', null, {
- color: Notes.inputData.notes.find(note => note.user === item).resolved ? 'green' : 'red',
- });
- const resolve = createElement('a', Notes.inputData.notes.find(note => note.user === item).resolved ? '[update]' : '[resolve]', null, {
- cursor: 'pointer',
- });
- resolve.addEventListener('click', () => {
- Notes.importNotesConflictResolve(item, resultsContainer, container, blurredBackground);
- })
- row.appendChild(resolve);
- overwrittenContainer.appendChild(row);
- }
- resultsContainer.appendChild(overwrittenContainer);
- }
-
- if (willBeAdded.length) {
- const addedContainer = createElement('div', 'Note will be added for: ' + willBeAdded.join(' '), null, {
- marginTop: '1rem',
- color: 'white'
- })
- resultsContainer.appendChild(addedContainer);
- }
-
- if (noChanges.length) {
- const noChangesContainer = createElement('div', 'No changes for: ' + noChanges.join(' '), null, {
- marginTop: '1rem',
- color: 'gray'
- })
- resultsContainer.appendChild(noChangesContainer);
- }
-
- const spacer = createElement('div', null, null, {height: '24px'});
- resultsContainer.appendChild(spacer);
-
- willBeOverwritten = willBeOverwritten.filter(x => !Notes.inputData.notes.find(n => n.user === x).resolved);
-
- const saveButton = createElement('button', 'Complete import', 'twitch-notes-settings-button',
- willBeOverwritten.length > 0 ? {background: 'gray'} : null,
- willBeOverwritten.length > 0 ? {disabled: 'true'} : null)
-
- resultsContainer.appendChild(saveButton)
-
- if (willBeOverwritten.length > 0) {
- const note = createElement('div', 'Import cannot be completed while there are unresolved conflicts ', null, {
- color: 'gray',
- });
- resultsContainer.appendChild(note);
- } else {
- saveButton.addEventListener('click', () => {
- for (let note of Notes.inputData.notes) {
- Notes.saveNote(note.user, note.note);
- }
- blurredBackground.remove();
- Notes.inputData = {};
- });
- }
-
- container.appendChild(resultsContainer);
- },
- importNotes: () => {
- const blurredBackground = createElement('div', null, 'twitch-notes-blurred-background');
- const container = createElement('div', 'WARNING! This action might override existing data! ', 'twitch-notes-center-floating-container');
- const inputBlock = createElement('div', 'Select exported twitch notes file ');
- const input = createElement('input', null, 'twitch-notes-file-input', null, {
- type: 'file',
- accept: '.json'
- });
- const settingsCloseButton = createElement('button', xButton, 'twitch-notes-settings-close-button', {
- position: 'absolute',
- top: '1rem',
- right: '1rem',
- });
- inputBlock.appendChild(input);
- settingsCloseButton.addEventListener('click', () => {
- blurredBackground.remove();
- });
- input.addEventListener('change', () => {
- let file = input.files[0];
- if (!file) {
- return;
- }
- let reader = new FileReader();
- reader.readAsText(file, "UTF-8");
- reader.onload = function (evt) {
- Notes.inputData = JSON.parse(evt.target.result.toString());
- if (Notes.inputData) {
- inputBlock.style.display = 'none';
- Notes.importNotesResult(container, blurredBackground);
- }
- }
- reader.onerror = function () {
- console.error("error reading file");
- }
- });
-
- container.appendChild(settingsCloseButton);
- container.appendChild(inputBlock);
- blurredBackground.appendChild(container);
- document.body.appendChild(blurredBackground);
- },
- clearAllData: () => {
- const blurredBackground = createElement('div', null, 'twitch-notes-blurred-background');
- const container = createElement('div', 'This action will delete all saved notes! Consider exporting notes before performing this action in case you will need to use notes again ', 'twitch-notes-center-floating-container');
-
- const spacer = createElement('div', null, null, {
- height: '24px'
- });
- const deleteAction = createElement('button', 'DELETE ALL NOTES', 'twitch-notes-settings-button', {
- background: 'red'
- });
- deleteAction.addEventListener('click', () => {
- const users = Notes.getSavedUserList();
- for(let user of users) {
- Notes.deleteNote(user);
- }
- blurredBackground.remove();
- })
- const cancelAction = createElement('button', 'Cancel', 'twitch-notes-settings-button');
- cancelAction.addEventListener('click', () => {
- blurredBackground.remove();
- })
-
- container.appendChild(spacer);
- container.appendChild(deleteAction);
- container.appendChild(cancelAction);
-
- blurredBackground.appendChild(container);
- document.body.appendChild(blurredBackground);
- },
- openAllNotes: () => {
- Notes.showAllNotes();
- }
- }
-
- const Mouse = {
- mousePosition: {x: 0, y: 0},
- lastMousePosition: {x: 0, y: 0},
- handleMouseMove: (event) => {
- if (isMouseDown) {
- if (activeContainer) {
- const dx = Mouse.mousePosition.x - Mouse.lastMousePosition.x;
- const dy = Mouse.mousePosition.y - Mouse.lastMousePosition.y;
- openContainers[activeContainer].style.top =
- parseInt(openContainers[activeContainer].style.top) + dy + "px";
- openContainers[activeContainer].style.left =
- parseInt(openContainers[activeContainer].style.left) + dx + "px";
- }
- }
- Mouse.updateMousePosition(event.clientX, event.clientY);
- },
- handleMouseUp: () => {
- activeContainer = null;
- isMouseDown = false;
- },
- updateMousePosition: (x, y) => {
- Mouse.lastMousePosition = {...Mouse.mousePosition};
- Mouse.mousePosition = {x, y};
- }
- }
-
- function createElement(tag, body, classes, styles, attributes, id) {
- const element = document.createElement(tag);
-
- if (body) {
- element.innerHTML = body;
- }
-
- if (classes) {
- if (typeof classes === 'object') {
- for (let className of classes) {
- element.classList.add((className));
- }
- } else {
- element.classList.add(classes);
- }
- }
-
- if (styles) {
- if (typeof styles === 'object') {
- for (let style in styles) {
- element.style[style] = styles[style];
- }
- }
- }
-
- if (attributes) {
- if (typeof attributes === 'object') {
- for (let attribute in attributes) {
- element.setAttribute(attribute, attributes[attribute]);
- }
- }
- }
-
- element.setAttribute('id', id ? id : 'element-' + Math.floor((Math.random() * 10000000)))
-
- return element;
- }
-
- function createSettingsButton(buttonContainer) {
-
- const c1 = createElement('div', null, null, {marginLeft: "0.5rem !important"});
- const c2 = createElement('div', null, null, {display: "inline-flex !important"});
- const settingsButton = createElement('button', 'Notes', 'twitch-notes-settings-button');
-
- settingsButton.addEventListener("click", toggleSettings);
-
- c2.appendChild(settingsButton);
- c1.appendChild(c2);
-
- buttonContainer.lastChild.insertBefore(
- c1,
- buttonContainer.lastChild.lastChild
- );
- }
-
- 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) {
-
- const container = createElement('div', null, 'twitch-note-container', {
- left: Mouse.mousePosition.x + 'px',
- top: Mouse.mousePosition.y + 10 + 'px'
- });
-
- const header = createElement('div', null, 'twitch-note-header');
- header.addEventListener("mousedown", function close() {
- activeContainer = username;
- isMouseDown = true;
- });
-
- const closeButton = createElement('span', xButton, 'twitch-note-close-button')
- closeButton.addEventListener("click", function close() {
- removeContainer(username);
- });
-
- const title = createElement('span', username, 'twitch-note-title');
- const content = createElement('div', Notes.getNote(username), 'twitch-note-content', [], {
- contentEditable: 'true'
- });
-
- const saveButton = createElement('div', 'SAVE', 'twitch-note-save-button')
- saveButton.addEventListener("click", function () {
- Notes.saveNote(username, content.innerHTML);
- removeContainer(username);
- });
-
- header.appendChild(closeButton);
- header.appendChild(title);
- container.appendChild(header);
- container.appendChild(content);
- container.appendChild(saveButton);
-
- addContainer(username, container);
- }
-
- function addNoteBadges() {
- 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(".twitch-note");
- if (!noteButton) {
-
- const twitchNote = createElement('span', null, 'twitch-note', {
- cursor: 'pointer',
- })
- twitchNote.addEventListener("click", () => {
- openTwitchNote(username);
- });
-
- const img = createElement('img', null, null, {
- height: '18px',
- paddingRight: '4px'
- }, {
- 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]++;
- }
- }
- }
-
- function createChatSettingsLine(text) {
- const element = createElement('div', null, 'twitch-notes-settings-option-line');
- const button = createElement('button', null, 'twitch-notes-settings-option-line-button');
- const buttonContainer = createElement('div', null, 'twitch-notes-settings-option-line-button-container');
- const buttonContainerContent = createElement('div', text, 'twitch-notes-settings-option-line-button-container-content');
- buttonContainer.appendChild(buttonContainerContent);
- button.appendChild(buttonContainer);
- element.appendChild(button);
-
- return element;
- }
-
- function createChatSettingsSeparator() {
- return createElement('div', null, 'twitch-note-settings-separator');
- }
-
- function toggleSettings() {
- if (!settingsWindow) {
- const settingsContainer = createElement('div', null, 'twitch-notes-settings-container');
- const settingsBalloon = createElement('div', null, 'twitch-notes-settings-balloon');
- const settingsPopover = createElement('div', null, 'twitch-notes-settings-popover');
-
- // startOf: settings header
- const settingsHeader = createElement('div', null, 'twitch-notes-settings-header');
- const settingsHeaderLeftElement = createElement('div', null, 'twitch-notes-settings-header-left-element')
- settingsHeader.appendChild(settingsHeaderLeftElement);
- const settingsHeaderCenterElement = createElement('div', null, 'twitch-notes-settings-header-center-element')
- settingsHeader.appendChild(settingsHeaderCenterElement);
- const settingsHeaderCenterElementContent = createElement('p', 'Twitch Notes Settings', 'twitch-notes-settings-header-center-element-content')
- settingsHeaderCenterElement.appendChild(settingsHeaderCenterElementContent);
- const settingsHeaderRightElement = createElement('div', null, 'twitch-notes-settings-header-right-element')
- settingsHeader.appendChild(settingsHeaderRightElement);
- const settingsCloseButton = createElement('button', xButton, 'twitch-notes-settings-close-button');
- settingsHeaderRightElement.appendChild(settingsCloseButton);
- settingsPopover.appendChild(settingsHeader);
- // endOf: settings header
-
- settingsCloseButton.addEventListener('click', toggleSettings);
-
-
- // startOf: scrollable area
- const settingsScrollableArea = createElement('div', null, 'twitch-notes-settings-scrollable-area');
- const settingsContent = createElement('div', null, 'twitch-notes-settings-content');
- settingsScrollableArea.appendChild(settingsContent);
-
- const exportNotes = createChatSettingsLine('Export Notes');
- settingsContent.appendChild(exportNotes);
- const importNotes = createChatSettingsLine('Import Notes');
- settingsContent.appendChild(importNotes);
- const clearData = createChatSettingsLine('Clear Data');
- settingsContent.appendChild(clearData);
- settingsContent.appendChild(createChatSettingsSeparator());
- const openAllNotes = createChatSettingsLine('View All Notes');
- settingsContent.appendChild(openAllNotes);
-
- exportNotes.addEventListener('click', () => {Notes.exportNotes(); toggleSettings()});
- importNotes.addEventListener('click', () => {Notes.importNotes(); toggleSettings()});
- clearData.addEventListener('click', () => {Notes.clearAllData(); toggleSettings()});
- openAllNotes.addEventListener('click', () => {Notes.openAllNotes(); toggleSettings()});
-
-
- settingsPopover.appendChild(settingsScrollableArea);
- // endOf: scrollable area
-
-
- settingsBalloon.appendChild(settingsPopover);
- settingsContainer.appendChild(settingsBalloon);
- settingsWindow = settingsContainer;
- document.body.appendChild(settingsWindow);
- settingsWindowOpen = true;
- return;
- }
-
- if (settingsWindowOpen) {
- settingsWindow.style.display = 'none';
- settingsWindowOpen = false;
- } else {
- settingsWindow.style.display = 'block';
- settingsWindowOpen = true;
- }
- }
-
- const xButton = ` `;
- let openContainers = {};
- let isMouseDown = false;
- let activeContainer = null;
- let settingsWindowOpen = false;
- let settingsWindow = null;
-
- let LS_UserList = "twitch-note-all-users-list";
- let LS_Prefix = "twitch-note-";
-
- document.addEventListener("mousemove", Mouse.handleMouseMove);
- document.addEventListener("mouseup", Mouse.handleMouseUp);
-
- 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());
-
- createSettingsButton(buttonContainer);
-
- // 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) => {
- if (!changeInProgress) {
- for (const mutation of mutationList) {
- if (mutation.type === "childList") {
- changeInProgress = true;
- addNoteBadges();
- 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);
-})();
diff --git a/extension/twitch-notes.js b/extension/twitch-notes.js
index fed0ad6..c4e9b9e 100644
--- a/extension/twitch-notes.js
+++ b/extension/twitch-notes.js
@@ -1 +1 @@
-(()=>{"use strict";var t={144:(t,e,o)=>{o.d(e,{Z:()=>s});var r=o(81),n=o.n(r),i=o(645),a=o.n(i)()(n());a.push([t.id,".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-ratio: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;cursor:pointer}.twitch-note-content{position:absolute;top:32px;left:0;right:0;bottom:32px;background:rgba(0,0,0,0);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}.twitch-notes-settings-button{z-index:var(--z-index-default) !important;position:relative !important;background-color:var(--color-background-button-primary-default);color:var(--color-text-button-primary);text-decoration:none;cursor:pointer;display:inline-flex;-webkit-box-align:center;align-items:center;-webkit-box-pack:center;justify-content:center;vertical-align:middle;overflow:hidden;white-space:nowrap;user-select:none;font-weight:var(--font-weight-semibold);border-radius:var(--border-radius-medium);font-size:var(--button-text-default);height:var(--button-size-default);padding:0 var(--button-padding-x)}.twitch-notes-settings-container{border:0;font:inherit;padding:0;vertical-align:baseline;position:fixed;z-index:var(--z-index-balloon);inset:auto 0 0 auto;margin:0 69px 48px 0}.twitch-notes-settings-balloon{display:inline-block;max-width:90vw;min-width:0;white-space:nowrap;border-radius:.6rem !important;background-color:var(--color-background-base) !important;box-shadow:var(--shadow-elevation-2) !important;color:inherit !important}.twitch-notes-settings-popover{white-space:normal;width:32rem}.twitch-notes-settings-header{display:flex !important;-webkit-box-align:center !important;align-items:center !important;position:relative !important;padding-left:1rem !important;padding-right:1rem !important;background-color:var(--color-background-base) !important;min-height:4rem}.twitch-notes-settings-header-left-element{left:0 !important;margin-right:.5rem !important;width:3rem}.twitch-notes-settings-header-center-element{-webkit-box-align:center !important;align-items:center !important;display:flex !important;text-align:center !important;-webkit-box-flex:1 !important;flex-grow:1 !important;-webkit-box-pack:center !important;justify-content:center !important}.twitch-notes-settings-header-center-element-content{line-height:1.5;color:var(--color-text-alt) !important;font-size:var(--font-size-5) !important;font-weight:var(--font-weight-semibold) !important}.twitch-notes-settings-header-right-element{right:0 !important;margin-left:.5rem !important}.twitch-notes-settings-close-button{cursor:default;text-transform:none;text-indent:0;text-shadow:none;letter-spacing:normal;text-rendering:auto;appearance:auto;writing-mode:horizontal-tb !important;box-sizing:border-box;margin:0;font:inherit;border:none;font-size:var(--button-text-default);font-weight:var(--font-weight-semibold);vertical-align:middle;overflow:hidden;text-decoration:none;white-space:nowrap;position:relative;display:inline-flex;-webkit-box-align:center;align-items:center;-webkit-box-pack:center;justify-content:center;user-select:none;border-radius:var(--border-radius-medium);height:calc(var(--button-size-default) - 1rem);width:calc(var(--button-size-default) - 1rem);background-color:var(--color-background-button-text-default);color:var(--color-fill-button-icon)}.twitch-notes-settings-scrollable-area{display:flex;overflow:hidden;z-index:0;height:100%;position:relative;max-height:478px}.twitch-notes-settings-content{box-sizing:content-box;min-width:100%;overflow-x:hidden;overflow-y:auto;max-height:inherit !important;margin:1rem;padding-bottom:0 !important}.twitch-notes-settings-option-line{position:relative !important;width:calc(100% - 2rem) !important}.twitch-notes-settings-option-line-button{border-radius:var(--border-radius-medium);display:block;width:100%;color:inherit}.twitch-notes-settings-option-line-button:hover{cursor:pointer;text-decoration:none;color:inherit;background-color:var(--color-background-interactable-hover)}.twitch-notes-settings-option-line-button-container{display:flex !important;-webkit-box-align:center !important;align-items:center !important;position:relative !important;padding:.5rem !important}.twitch-notes-settings-option-line-button-container-content{-webkit-box-flex:1 !important;flex-grow:1 !important}.twitch-note-settings-separator{border-top:1px solid var(--color-border-base);margin-top:1rem !important;margin-left:.5rem !important;margin-right:.5rem !important;padding-bottom:1rem !important}.twitch-notes-x-button{position:absolute;left:0;width:100%;min-height:100%;top:0;fill:currentcolor}.twitch-notes-blurred-background{position:fixed;inset:0 0 0 0;z-index:2000000;background:rgba(0,0,0,.4);backdrop-filter:blur(2px)}.twitch-notes-center-floating-container{background:var(--color-background-base);border:var(--border-width-default) solid var(--color-border-base) !important;padding:4rem 2rem 2rem;position:fixed;top:50vh;left:50vw;transform:translate(-50%, -50%);max-height:90vh;max-width:90vw;overflow:auto}.twitch-notes-user-list-user{padding:1rem;border-bottom:var(--border-width-default) solid var(--color-border-base) !important;cursor:pointer}.twitch-notes-user-list-user[data-active]{background:rgba(119,44,232,.2)}",""]);const s=a},645:t=>{t.exports=function(t){var e=[];return e.toString=function(){return this.map((function(e){var o="",r=void 0!==e[5];return e[4]&&(o+="@supports (".concat(e[4],") {")),e[2]&&(o+="@media ".concat(e[2]," {")),r&&(o+="@layer".concat(e[5].length>0?" ".concat(e[5]):""," {")),o+=t(e),r&&(o+="}"),e[2]&&(o+="}"),e[4]&&(o+="}"),o})).join("")},e.i=function(t,o,r,n,i){"string"==typeof t&&(t=[[null,t,void 0]]);var a={};if(r)for(var s=0;s0?" ".concat(d[5]):""," {").concat(d[1],"}")),d[5]=i),o&&(d[2]?(d[1]="@media ".concat(d[2]," {").concat(d[1],"}"),d[2]=o):d[2]=o),n&&(d[4]?(d[1]="@supports (".concat(d[4],") {").concat(d[1],"}"),d[4]=n):d[4]="".concat(n)),e.push(d))}},e}},81:t=>{t.exports=function(t){return t[1]}},379:t=>{var e=[];function o(t){for(var o=-1,r=0;r{var e={};t.exports=function(t,o){var r=function(t){if(void 0===e[t]){var o=document.querySelector(t);if(window.HTMLIFrameElement&&o instanceof window.HTMLIFrameElement)try{o=o.contentDocument.head}catch(t){o=null}e[t]=o}return e[t]}(t);if(!r)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");r.appendChild(o)}},216:t=>{t.exports=function(t){var e=document.createElement("style");return t.setAttributes(e,t.attributes),t.insert(e,t.options),e}},565:(t,e,o)=>{t.exports=function(t){var e=o.nc;e&&t.setAttribute("nonce",e)}},795:t=>{t.exports=function(t){var e=t.insertStyleElement(t);return{update:function(o){!function(t,e,o){var r="";o.supports&&(r+="@supports (".concat(o.supports,") {")),o.media&&(r+="@media ".concat(o.media," {"));var n=void 0!==o.layer;n&&(r+="@layer".concat(o.layer.length>0?" ".concat(o.layer):""," {")),r+=o.css,n&&(r+="}"),o.media&&(r+="}"),o.supports&&(r+="}");var i=o.sourceMap;i&&"undefined"!=typeof btoa&&(r+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(i))))," */")),e.styleTagTransform(r,t,e.options)}(e,t,o)},remove:function(){!function(t){if(null===t.parentNode)return!1;t.parentNode.removeChild(t)}(e)}}}},589:t=>{t.exports=function(t,e){if(e.styleSheet)e.styleSheet.cssText=t;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(t))}}}},e={};function o(r){var n=e[r];if(void 0!==n)return n.exports;var i=e[r]={id:r,exports:{}};return t[r](i,i.exports,o),i.exports}o.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return o.d(e,{a:e}),e},o.d=(t,e)=>{for(var r in e)o.o(e,r)&&!o.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:e[r]})},o.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),o.nc=void 0,(()=>{var t=o(379),e=o.n(t),r=o(795),n=o.n(r),i=o(569),a=o.n(i),s=o(565),c=o.n(s),l=o(216),d=o.n(l),p=o(589),u=o.n(p),m=o(144),h={};h.styleTagTransform=u(),h.setAttributes=c(),h.insert=a().bind(null,"head"),h.domAPI=n(),h.insertStyleElement=d(),e()(m.Z,h),m.Z&&m.Z.locals&&m.Z.locals;var b=new(function(){function t(){this.observer=null}return t.prototype.init=function(t){this.chatContainerNode=t,console.log("CHAT CONTAINER INITIATED"),this.startObserver(t),this.addNotesButton()},t.prototype.startObserver=function(t){var e=this;this.observer&&this.observer.disconnect();var o=!1;this.observer=new MutationObserver((function(t){if(!o)for(var r=0,n=t;r{"use strict";var t={144:(t,e,n)=>{n.d(e,{Z:()=>a});var o=n(81),i=n.n(o),r=n(645),s=n.n(r)()(i());s.push([t.id,".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-ratio: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;cursor:pointer}.twitch-note-content{position:absolute;top:32px;left:0;right:0;bottom:32px;background:rgba(0,0,0,0);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}.twitch-notes-settings-button{z-index:var(--z-index-default) !important;position:relative !important;background-color:var(--color-background-button-primary-default);color:var(--color-text-button-primary);text-decoration:none;cursor:pointer;display:inline-flex;-webkit-box-align:center;align-items:center;-webkit-box-pack:center;justify-content:center;vertical-align:middle;overflow:hidden;white-space:nowrap;user-select:none;font-weight:var(--font-weight-semibold);border-radius:var(--border-radius-medium);font-size:var(--button-text-default);height:var(--button-size-default);padding:0 var(--button-padding-x)}.twitch-notes-settings-container{border:0;font:inherit;padding:0;vertical-align:baseline;position:fixed;z-index:var(--z-index-balloon);inset:auto 0 0 auto;margin:0 69px 48px 0}.twitch-notes-settings-balloon{display:inline-block;max-width:90vw;min-width:0;white-space:nowrap;border-radius:.6rem !important;background-color:var(--color-background-base) !important;box-shadow:var(--shadow-elevation-2) !important;color:inherit !important}.twitch-notes-settings-popover{white-space:normal;width:32rem}.twitch-notes-settings-header{display:flex !important;-webkit-box-align:center !important;align-items:center !important;position:relative !important;padding-left:1rem !important;padding-right:1rem !important;background-color:var(--color-background-base) !important;min-height:4rem}.twitch-notes-settings-header-left-element{left:0 !important;margin-right:.5rem !important;width:3rem}.twitch-notes-settings-header-center-element{-webkit-box-align:center !important;align-items:center !important;display:flex !important;text-align:center !important;-webkit-box-flex:1 !important;flex-grow:1 !important;-webkit-box-pack:center !important;justify-content:center !important}.twitch-notes-settings-header-center-element-content{line-height:1.5;color:var(--color-text-alt) !important;font-size:var(--font-size-5) !important;font-weight:var(--font-weight-semibold) !important}.twitch-notes-settings-header-right-element{right:0 !important;margin-left:.5rem !important}.twitch-notes-settings-close-button{cursor:default;text-transform:none;text-indent:0;text-shadow:none;letter-spacing:normal;text-rendering:auto;appearance:auto;writing-mode:horizontal-tb !important;box-sizing:border-box;margin:0;font:inherit;border:none;font-size:var(--button-text-default);font-weight:var(--font-weight-semibold);vertical-align:middle;overflow:hidden;text-decoration:none;white-space:nowrap;position:relative;display:inline-flex;-webkit-box-align:center;align-items:center;-webkit-box-pack:center;justify-content:center;user-select:none;border-radius:var(--border-radius-medium);height:calc(var(--button-size-default) - 1rem);width:calc(var(--button-size-default) - 1rem);background-color:var(--color-background-button-text-default);color:var(--color-fill-button-icon)}.twitch-notes-settings-scrollable-area{display:flex;overflow:hidden;z-index:0;height:100%;position:relative;max-height:478px}.twitch-notes-settings-content{box-sizing:content-box;min-width:100%;overflow-x:hidden;overflow-y:auto;max-height:inherit !important;margin:1rem;padding-bottom:0 !important}.twitch-notes-settings-option-line{position:relative !important;width:calc(100% - 2rem) !important}.twitch-notes-settings-option-line-button{border-radius:var(--border-radius-medium);display:block;width:100%;color:inherit}.twitch-notes-settings-option-line-button:hover{cursor:pointer;text-decoration:none;color:inherit;background-color:var(--color-background-interactable-hover)}.twitch-notes-settings-option-line-button-container{display:flex !important;-webkit-box-align:center !important;align-items:center !important;position:relative !important;padding:.5rem !important}.twitch-notes-settings-option-line-button-container-content{-webkit-box-flex:1 !important;flex-grow:1 !important}.twitch-note-settings-separator{border-top:1px solid var(--color-border-base);margin-top:1rem !important;margin-left:.5rem !important;margin-right:.5rem !important;padding-bottom:1rem !important}.twitch-notes-x-button{position:absolute;left:0;width:100%;min-height:100%;top:0;fill:currentcolor}.twitch-notes-blurred-background{position:fixed;inset:0 0 0 0;z-index:2000000;background:rgba(0,0,0,.4);backdrop-filter:blur(2px)}.twitch-notes-center-floating-container{background:var(--color-background-base);border:var(--border-width-default) solid var(--color-border-base) !important;padding:4rem 2rem 2rem;position:fixed;top:50vh;left:50vw;transform:translate(-50%, -50%);max-height:90vh;max-width:90vw;overflow:auto}.twitch-notes-user-list-user{padding:1rem;border-bottom:var(--border-width-default) solid var(--color-border-base) !important;cursor:pointer}.twitch-notes-user-list-user[data-active]{background:rgba(119,44,232,.2)}",""]);const a=s},645:t=>{t.exports=function(t){var e=[];return e.toString=function(){return this.map((function(e){var n="",o=void 0!==e[5];return e[4]&&(n+="@supports (".concat(e[4],") {")),e[2]&&(n+="@media ".concat(e[2]," {")),o&&(n+="@layer".concat(e[5].length>0?" ".concat(e[5]):""," {")),n+=t(e),o&&(n+="}"),e[2]&&(n+="}"),e[4]&&(n+="}"),n})).join("")},e.i=function(t,n,o,i,r){"string"==typeof t&&(t=[[null,t,void 0]]);var s={};if(o)for(var a=0;a0?" ".concat(c[5]):""," {").concat(c[1],"}")),c[5]=r),n&&(c[2]?(c[1]="@media ".concat(c[2]," {").concat(c[1],"}"),c[2]=n):c[2]=n),i&&(c[4]?(c[1]="@supports (".concat(c[4],") {").concat(c[1],"}"),c[4]=i):c[4]="".concat(i)),e.push(c))}},e}},81:t=>{t.exports=function(t){return t[1]}},379:t=>{var e=[];function n(t){for(var n=-1,o=0;o{var e={};t.exports=function(t,n){var o=function(t){if(void 0===e[t]){var n=document.querySelector(t);if(window.HTMLIFrameElement&&n instanceof window.HTMLIFrameElement)try{n=n.contentDocument.head}catch(t){n=null}e[t]=n}return e[t]}(t);if(!o)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");o.appendChild(n)}},216:t=>{t.exports=function(t){var e=document.createElement("style");return t.setAttributes(e,t.attributes),t.insert(e,t.options),e}},565:(t,e,n)=>{t.exports=function(t){var e=n.nc;e&&t.setAttribute("nonce",e)}},795:t=>{t.exports=function(t){var e=t.insertStyleElement(t);return{update:function(n){!function(t,e,n){var o="";n.supports&&(o+="@supports (".concat(n.supports,") {")),n.media&&(o+="@media ".concat(n.media," {"));var i=void 0!==n.layer;i&&(o+="@layer".concat(n.layer.length>0?" ".concat(n.layer):""," {")),o+=n.css,i&&(o+="}"),n.media&&(o+="}"),n.supports&&(o+="}");var r=n.sourceMap;r&&"undefined"!=typeof btoa&&(o+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(r))))," */")),e.styleTagTransform(o,t,e.options)}(e,t,n)},remove:function(){!function(t){if(null===t.parentNode)return!1;t.parentNode.removeChild(t)}(e)}}}},589:t=>{t.exports=function(t,e){if(e.styleSheet)e.styleSheet.cssText=t;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(t))}}}},e={};function n(o){var i=e[o];if(void 0!==i)return i.exports;var r=e[o]={id:o,exports:{}};return t[o](r,r.exports,n),r.exports}n.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return n.d(e,{a:e}),e},n.d=(t,e)=>{for(var o in e)n.o(e,o)&&!n.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:e[o]})},n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),n.nc=void 0,(()=>{var t=n(379),e=n.n(t),o=n(795),i=n.n(o),r=n(569),s=n.n(r),a=n(565),l=n.n(a),d=n(216),c=n.n(d),u=n(589),p=n.n(u),h=n(144),m={};m.styleTagTransform=p(),m.setAttributes=l(),m.insert=s().bind(null,"head"),m.domAPI=i(),m.insertStyleElement=c(),e()(h.Z,m),h.Z&&h.Z.locals&&h.Z.locals;var f,v=(f=function(t,e){return f=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n])},f(t,e)},function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Class extends value "+String(e)+" is not a constructor or null");function n(){this.constructor=t}f(t,e),t.prototype=null===e?Object.create(e):(n.prototype=e.prototype,new n)}),g=function(){function t(t,e){if(this.element=document.createElement(t),e.id?this.setId(e.id):this.setId(t+"-generated-id-"+Math.floor(1e7*Math.random())),e.class)if("string"==typeof e.class)this.addClass(e.class);else for(var n=0,o=e.class;n\n \n \n \n '),c=new w("twitch-notes-settings-scrollable-area"),u=new w("twitch-notes-settings-content"),p=B("Export Notes"),h=B("Import Notes"),m=B("Clear Data"),f=B("View All Notes");return e.appendCustomChild(n),n.appendCustomChild(o),o.appendCustomChild(i),i.appendCustomChild(r),i.appendCustomChild(s),s.appendCustomChild(a),i.appendCustomChild(l),l.appendCustomChild(d),o.appendCustomChild(c),c.appendCustomChild(u),u.appendCustomChild(p),u.appendCustomChild(h),u.appendCustomChild(m),u.appendCustomChild(new w("twitch-note-settings-separator")),u.appendCustomChild(f),d.setClickListener((function(){return z()})),p.setClickListener((function(){N.exportNotes(),z()})),h.setClickListener((function(){!function(){var t=new w("twitch-notes-blurred-background"),e=new w("twitch-notes-center-floating-container","WARNING! This action might override existing data! "),n=new w("","Select exported twitch notes file "),o=new b("twitch-notes-file-input");o.addAttribute("type","file").addAttribute("accept","json");var i=new y("twitch-notes-settings-close-button",'\n \n \n \n ');i.setStyle({position:"absolute",top:"1rem",right:"1rem"}),n.appendCustomChild(o),i.setClickListener((function(){t.remove()})),o.setChangeListener((function(){var i;if(null===(i=null==o?void 0:o.getElement())||void 0===i?void 0:i.files){var r=o.getElement().files.item(0);if(r){var s=new FileReader;s.readAsText(r,"UTF-8"),s.onload=function(o){var i;(null===(i=null==o?void 0:o.target)||void 0===i?void 0:i.result)&&(N.inputData=JSON.parse(o.target.result.toString()),N.inputData&&(n.setStyle({display:"none"}),D(e,t)))},s.onerror=function(){console.error("error reading file")}}}})),e.appendCustomChild(i),e.appendCustomChild(n),t.appendCustomChild(e),document.body.appendChild(t.getElement())}(),z()})),m.setClickListener((function(){!function(){var t=new w("twitch-notes-blurred-background"),e=new w("twitch-notes-center-floating-container","This action will delete all saved notes! Consider exporting notes before performing this action in case you will need to use notes again "),n=new w;n.setStyle({height:"24px"});var o=new y("twitch-notes-settings-button","DELETE ALL NOTES");o.setStyle({background:"red"}),o.setClickListener((function(){N.clearData(),t.remove()}));var i=new y("twitch-notes-settings-button","Cancel");i.setClickListener((function(){t.remove()})),e.appendCustomChild(n),e.appendCustomChild(o),e.appendCustomChild(i),t.appendCustomChild(e),document.body.appendChild(t.getElement())}(),z()})),f.setClickListener((function(){!function(){var t="",e=new w("twitch-notes-blurred-background"),n=new S("","Note saved!");n.setStyle({color:"green",marginLeft:"2rem"}),n.hide();var o=new w("twitch-notes-center-floating-container");o.setStyle({padding:"0",paddingTop:"1rem",width:"100%",maxWidth:"800px",height:"calc(320px + 5rem)",maxHeight:"90vh"});var i=new y("twitch-notes-settings-close-button",'\n \n \n \n ');i.setStyle({position:"absolute",top:"1rem",right:"1rem"});var r=new w("twitch-note-title","Notes:");r.setStyle({borderBottom:"1px solid #FFFFFF19",paddingBottom:"1rem"}),o.appendCustomChild(r);var s=new w;s.setStyle({display:"flex",flexDirection:"row",flexWrap:"no-wrap",width:"100%",height:"320px"}),o.appendCustomChild(s),i.setClickListener((function(){e.remove()})),o.appendCustomChild(i);var a=new w;a.setStyle({borderRight:"1px solid #FFFFFF19",height:"320px",minWidth:"240px",overflowY:"auto",overflowX:"hidden"});var l=new w;l.setStyle({height:"320px",padding:"1rem",width:"100%",opacity:"0"});var d=new w;d.setStyle({marginBottom:"3rem",border:"1px solid #FFFFFF19",height:"calc(320px - 7rem)",width:"100%",padding:"6px",outline:"none",overflowY:"auto",overflowX:"hidden"}),d.addAttribute("contentEditable","true");var c=new y("twitch-notes-settings-button","Save Note");c.setStyle({position:"absolute",bottom:"1rem",left:"1rem"}),c.setClickListener((function(){t&&(N.saveNote(t,d.getElement().innerHTML),n.setStyle({display:"inline-block"}))}));for(var u=function(e){var o=new w("twitch-notes-user-list-user",e);o.setClickListener((function(){for(var i,r=document.getElementsByClassName("twitch-notes-user-list-user"),s=0;sNote conflicts found for: ");p.setStyle({marginTop:"1rem"});for(var h=function(n){var o=new w("",n+" "),r=null===(i=N.inputData.notes.find((function(t){return t.user===n})))||void 0===i?void 0:i.resolved;o.setStyle({color:r?"green":"red"});var s=new x("",r?"[update]":"[resolve]");s.setStyle({cursor:"pointer"}),s.setClickListener((function(){!function(t,e,n,o){var i=new w("twitch-notes-blurred-background"),r=new w("twitch-notes-center-floating-container","Select which note to keep for "+t+" You can modify notes to merge them and keep updated one "),s=new w;s.setStyle({display:"flex",flexDirection:"row",flexWrap:"no-wrap",width:"100%"});var a={minWidth:"320px",padding:"6px",border:"1px solid #FFFFFF19",outline:"none"},l=["contentEditable","true"],d=new w,c=new C("","Locally saved note ");d.appendCustomChild(c);var u=new w;u.setStyle(a),u.addAttribute.apply(u,l),u.setId("local-value-container");var p=new y("twitch-notes-settings-button","Save this");p.setClickListener((function(){var r,s=(null===(r=document.getElementById("local-value-container"))||void 0===r?void 0:r.innerHTML)||"";N.inputData.notes=N.inputData.notes.map((function(e){return e.user===t?{user:e.user,note:s,resolved:!0}:e})),i.remove(),e.remove(),D(n,o)})),d.appendCustomChild(u),d.appendBody(" "),d.appendCustomChild(p);var h=new w,m=new C("","Imported note ");h.appendCustomChild(m);var f=new w("",N.inputData.notes.filter((function(e){return e.user===t}))[0].note||"");f.setId("import-value-container"),f.setStyle(a),f.addAttribute.apply(f,l);var v=new y("twitch-notes-settings-button","Save this");v.setClickListener((function(){var r,s=(null===(r=document.getElementById("import-value-container"))||void 0===r?void 0:r.innerHTML)||"";N.inputData.notes=N.inputData.notes.map((function(e){return e.user===t?{user:e.user,note:s,resolved:!0}:e})),i.remove(),e.remove(),D(n,o)})),h.appendCustomChild(f),h.appendBody(" "),h.appendCustomChild(v),s.appendCustomChild(d),s.appendCustomChild(h),r.appendCustomChild(s),i.appendCustomChild(r),document.body.appendChild(i.getElement())}(n,u,t,e)})),o.appendCustomChild(s),p.appendCustomChild(o)},m=0,f=s;mNote will be added for: "+r.join(" "));v.setStyle({marginTop:"1rem",color:"white"}),u.appendCustomChild(v)}if(a.length){var g=new w("","No changes for: "+a.join(" "));g.setStyle({marginTop:"1rem",color:"gray"}),u.appendCustomChild(g)}var b=new w;b.setStyle({height:"24px"}),u.appendCustomChild(b),s=s.filter((function(t){var e;return!(null===(e=N.inputData.notes.find((function(e){return e.user===t})))||void 0===e?void 0:e.resolved)}));var S=new y("twitch-notes-settings-button","Complete import");if(S.setStyle({}),s.length>0&&S.setStyle({background:"gray"}),s.length>0&&S.addAttribute("disabled","true"),u.appendCustomChild(S),s.length>0){var L=new w("","Import cannot be completed while there are unresolved conflicts ");L.setStyle({color:"gray"}),u.appendCustomChild(L)}else S.setClickListener((function(){for(var t=0,n=N.inputData.notes;t\n \n \n \n ');o.setClickListener((function(){M.removeContainer(t)}));var i=new S("twitch-note-title",t),r=new w("twitch-note-content",N.getNote(t));r.addAttribute("contentEditable","true");var s=new w("twitch-note-save-button","SAVE");s.setClickListener((function(){N.saveNote(t,r.getElement().innerHTML),M.removeContainer(t)})),n.appendCustomChild(o),n.appendCustomChild(i),e.appendCustomChild(n),e.appendCustomChild(r),e.appendCustomChild(s),document.body.appendChild(e.getElement()),M.addContainer(t,e)}}(r)}));var a=new L;a.setStyle({height:"18px",paddingRight:"4px"}),a.addAttribute("src","https://cdn.rdarius.lt/icons/32-id-card.png"),s.appendCustomChild(a),i.insertBefore(s.getElement(),i.firstChild)}},n=0;n {
+
+ element: HTMLElement;
+
+ constructor(element: string, data: CustomHTMLElementConstructor) {
+ this.element = document.createElement(element);
+
+ if (data.id) {
+ this.setId(data.id);
+ } else {
+ this.setId(element + '-generated-id-' + Math.floor(Math.random() * 10000000));
+ }
+
+ if (data.class) {
+ if (typeof data.class === "string") {
+ this.addClass(data.class);
+ } else {
+ for (const className of data.class) {
+ this.addClass(className);
+ }
+ }
+ }
+
+ if (data.attributes) {
+ for (const key in data.attributes) {
+ this.addAttribute(key, data.attributes[key]);
+ }
+ }
+
+ if (data.css) this.setStyle(data.css);
+
+ if (data.body) typeof data.body === "string" ? this.setBody(data.body) : this.appendChild(data.body);
+ if (data.customBody) this.appendCustomChild(data.customBody);
+ }
+
+ setId(id: string): CustomHTMLElement {
+ this.element.id = id;
+ return this;
+ }
+
+ addClass(classes: string): CustomHTMLElement {
+ this.element.classList.add(classes);
+ return this;
+ }
+
+ addAttribute(name: string, value: string): CustomHTMLElement {
+ this.element.setAttribute(name, value);
+ return this;
+ }
+
+ setStyle(attributes: Partial): CustomHTMLElement {
+ for(let key in attributes) {
+ this.element.style[key] = attributes[key] || '';
+ }
+ return this;
+ }
+
+ setBody(body: string): CustomHTMLElement {
+ this.element.innerHTML = body;
+ return this;
+ }
+
+ appendBody(body: string): CustomHTMLElement {
+ this.element.innerHTML += body;
+ return this;
+ }
+
+ appendChild(child: HTMLElement): CustomHTMLElement {
+ this.element.appendChild(child);
+ return this;
+ }
+
+ appendCustomChild(child: CustomHTMLElement): CustomHTMLElement {
+ this.element.appendChild(child.getElement());
+ return this;
+ }
+
+ getElement(): T {
+ return this.element as T;
+ }
+
+ setClickListener(listener: Function): CustomHTMLElement {
+ this.element.addEventListener('click', () => listener());
+ return this;
+ }
+
+ setChangeListener(listener: Function): CustomHTMLElement {
+ this.element.addEventListener('change', () => listener());
+ return this;
+ }
+
+ setMouseMoveListener(listener: Function): CustomHTMLElement {
+ this.element.addEventListener('mousemove', () => listener());
+ return this;
+ }
+
+ setMouseDownListener(listener: Function): CustomHTMLElement {
+ this.element.addEventListener('mousedown', () => listener());
+ return this;
+ }
+
+ setMouseUpListener(listener: Function): CustomHTMLElement {
+ this.element.addEventListener('mouseup', () => listener());
+ return this;
+ }
+
+ remove(): void {
+ this.element.remove();
+ }
+
+ show(): CustomHTMLElement {
+ this.setStyle({
+ display: 'unset',
+ });
+ return this;
+ }
+
+ hide(): CustomHTMLElement {
+ this.setStyle({
+ display: 'none',
+ });
+ return this;
+ }
+
+}
+
+export class CustomDivElement extends CustomHTMLElement {
+ constructor(className: string = '', content: string = '') {
+ super('div', {class: className, body: content});
+ }
+}
+export class CustomInputElement extends CustomHTMLElement {
+ constructor(className: string = '') {
+ super('input', {class: className});
+ }
+}
+
+export class CustomPElement extends CustomHTMLElement {
+ constructor(className: string = '', content: string = '') {
+ super('p', {class: className, body: content});
+ }
+}
+
+export class CustomButtonElement extends CustomHTMLElement {
+ constructor(className: string = '', content: string = '') {
+ super('button', {class: className, body: content});
+ }
+}
+
+export class CustomAElement extends CustomHTMLElement {
+ constructor(className: string = '', content: string = '') {
+ super('a', {class: className, body: content});
+ }
+}
+
+export class CustomSpanElement extends CustomHTMLElement {
+ constructor(className: string = '', content: string = '') {
+ super('span', {class: className, body: content});
+ }
+}
+
+export class CustomImgElement extends CustomHTMLElement {
+ constructor(className: string = '', content: string = '') {
+ super('img', {class: className, body: content});
+ }
+}
\ No newline at end of file
diff --git a/src/HTMLTemplates.ts b/src/HTMLTemplates.ts
new file mode 100644
index 0000000..95367f5
--- /dev/null
+++ b/src/HTMLTemplates.ts
@@ -0,0 +1,509 @@
+import {
+ CustomAElement,
+ CustomButtonElement,
+ CustomDivElement,
+ CustomHTMLElement,
+ CustomInputElement,
+ CustomPElement, CustomSpanElement
+} from "./CustomHTMLElement";
+import NoteStorage from "./NoteStorage";
+import Mouse from "./Mouse";
+import NoteContainers from "./NoteContainers";
+
+export function toggleSettingsList() {
+ const settingsWindow = document.querySelector('.twitch-notes-settings-container') as HTMLElement;
+ if (!settingsWindow) {
+ const settingsContainer = new CustomDivElement('twitch-notes-settings-container');
+ const settingsBalloon = new CustomDivElement('twitch-notes-settings-balloon');
+ const settingsPopover = new CustomDivElement('twitch-notes-settings-popover');
+ const settingsHeader = new CustomDivElement('twitch-notes-settings-header');
+ const settingsHeaderLeftElement = new CustomDivElement('twitch-notes-settings-header-left-element');
+ const settingsHeaderCenterElement = new CustomDivElement('twitch-notes-settings-header-center-element');
+ const settingsHeaderCenterElementContent = new CustomPElement('twitch-notes-settings-header-center-element-content','Twitch Notes Settings');
+ const settingsHeaderRightElement = new CustomDivElement('twitch-notes-settings-header-right-element');
+ const settingsCloseButton = new CustomButtonElement('twitch-notes-settings-close-button', getCloseButtonSVG());
+ const settingsScrollableArea = new CustomDivElement('twitch-notes-settings-scrollable-area');
+ const settingsContent = new CustomDivElement('twitch-notes-settings-content');
+ const exportNotes = createChatSettingsLine('Export Notes');
+ const importNotes = createChatSettingsLine('Import Notes');
+ const clearData = createChatSettingsLine('Clear Data');
+ const openAllNotes = createChatSettingsLine('View All Notes');
+
+ settingsContainer.appendCustomChild(settingsBalloon);
+ settingsBalloon.appendCustomChild(settingsPopover);
+ settingsPopover.appendCustomChild(settingsHeader);
+ settingsHeader.appendCustomChild(settingsHeaderLeftElement);
+ settingsHeader.appendCustomChild(settingsHeaderCenterElement);
+ settingsHeaderCenterElement.appendCustomChild(settingsHeaderCenterElementContent);
+ settingsHeader.appendCustomChild(settingsHeaderRightElement);
+ settingsHeaderRightElement.appendCustomChild(settingsCloseButton);
+ settingsPopover.appendCustomChild(settingsScrollableArea);
+ settingsScrollableArea.appendCustomChild(settingsContent);
+ settingsContent.appendCustomChild(exportNotes);
+ settingsContent.appendCustomChild(importNotes);
+ settingsContent.appendCustomChild(clearData);
+ settingsContent.appendCustomChild(createChatSettingsSeparator());
+ settingsContent.appendCustomChild(openAllNotes);
+
+ settingsCloseButton.setClickListener(() => toggleSettingsList());
+ exportNotes.setClickListener(() => {
+ NoteStorage.exportNotes();
+ toggleSettingsList();
+ });
+ importNotes.setClickListener(() => {
+ openImportNotesWindow();
+ toggleSettingsList();
+ });
+ clearData.setClickListener(() => {
+ openClearDataWindow();
+ toggleSettingsList();
+ });
+ openAllNotes.setClickListener(() => {
+ openAllNoteListWindow();
+ toggleSettingsList();
+ });
+
+ document.body.appendChild(settingsContainer.getElement());
+ return;
+ } else {
+ settingsWindow.remove();
+ }
+}
+
+export function openAllNoteListWindow() {
+ let activeNote: string = '';
+ const blurredBackground = new CustomDivElement('twitch-notes-blurred-background');
+
+ const noteSaved = new CustomSpanElement('', 'Note saved!');
+ noteSaved.setStyle({
+ color: 'green',
+ marginLeft: '2rem',
+ });
+ noteSaved.hide();
+
+ const container = new CustomDivElement('twitch-notes-center-floating-container');
+ container.setStyle({
+ padding: '0',
+ paddingTop: '1rem',
+ width: '100%',
+ maxWidth: '800px',
+ height: 'calc(320px + 5rem)',
+ maxHeight: '90vh',
+ });
+
+ const closeButton = new CustomButtonElement('twitch-notes-settings-close-button', getCloseButtonSVG());
+ closeButton.setStyle({
+ position: 'absolute',
+ top: '1rem',
+ right: '1rem',
+ });
+
+ const title = new CustomDivElement('twitch-note-title', 'Notes:');
+ title.setStyle({
+ borderBottom: '1px solid #FFFFFF19',
+ paddingBottom: '1rem',
+ });
+ container.appendCustomChild(title);
+
+ const content = new CustomDivElement();
+ content.setStyle({
+ display: 'flex',
+ flexDirection: 'row',
+ flexWrap: 'no-wrap',
+ width: '100%',
+ height: '320px'
+ });
+ container.appendCustomChild(content);
+ closeButton.setClickListener(() => {
+ blurredBackground.remove();
+ });
+ container.appendCustomChild(closeButton);
+
+ const userList = new CustomDivElement();
+ userList.setStyle({
+ borderRight: '1px solid #FFFFFF19',
+ height: '320px',
+ minWidth: '240px',
+ overflowY: 'auto',
+ overflowX: 'hidden',
+ });
+
+ const noteContainer = new CustomDivElement();
+ noteContainer.setStyle({
+ height: '320px',
+ padding: '1rem',
+ width: '100%',
+ opacity: '0',
+ });
+
+ const note = new CustomDivElement();
+ note.setStyle({
+ marginBottom: '3rem',
+ border: '1px solid #FFFFFF19',
+ height: 'calc(320px - 7rem)',
+ width: '100%',
+ padding: '6px',
+ outline: 'none',
+ overflowY: 'auto',
+ overflowX: 'hidden',
+ });
+ note.addAttribute('contentEditable', 'true');
+
+ const saveNote = new CustomButtonElement('twitch-notes-settings-button','Save Note');
+ saveNote.setStyle({
+ position: 'absolute',
+ bottom: '1rem',
+ left: '1rem'
+ });
+
+ saveNote.setClickListener(() => {
+ if(!activeNote) {
+ return;
+ }
+ NoteStorage.saveNote(activeNote, note.getElement().innerHTML);
+ noteSaved.setStyle({
+ display: 'inline-block',
+ });
+ });
+
+ for(let user of NoteStorage.getSavedUserList()) {
+ const userLine = new CustomDivElement('twitch-notes-user-list-user', user);
+ userLine.setClickListener(() => {
+ const list = document.getElementsByClassName('twitch-notes-user-list-user');
+ for(let i = 0; i < list.length; i++) {
+ list.item(i)?.removeAttribute('data-active');
+ }
+ noteSaved.hide();
+ userLine.addAttribute('data-active', 'true');
+ noteContainer.setStyle({opacity: '1'});
+ note.getElement().innerHTML = NoteStorage.getNote(user);
+ activeNote = user;
+ });
+ userList.appendCustomChild(userLine);
+ }
+
+ noteContainer.appendCustomChild(note);
+ noteContainer.appendCustomChild(saveNote);
+ noteContainer.appendCustomChild(noteSaved);
+
+ content.appendCustomChild(userList);
+ content.appendCustomChild(noteContainer);
+
+ blurredBackground.appendCustomChild(container);
+ document.body.appendChild(blurredBackground.getElement());
+}
+
+export function openClearDataWindow() {
+ const blurredBackground = new CustomDivElement( 'twitch-notes-blurred-background');
+ const container = new CustomDivElement('twitch-notes-center-floating-container', 'This action will delete all saved notes! Consider exporting notes before performing this action in case you will need to use notes again ');
+ const spacer = new CustomDivElement();
+ spacer.setStyle({
+ height: '24px'
+ });
+ const deleteAction = new CustomButtonElement('twitch-notes-settings-button', 'DELETE ALL NOTES');
+ deleteAction.setStyle({
+ background: 'red'
+ });
+ deleteAction.setClickListener(() => {
+ NoteStorage.clearData();
+ blurredBackground.remove();
+ });
+ const cancelAction = new CustomButtonElement('twitch-notes-settings-button', 'Cancel');
+ cancelAction.setClickListener(() => {
+ blurredBackground.remove();
+ });
+ container.appendCustomChild(spacer);
+ container.appendCustomChild(deleteAction);
+ container.appendCustomChild(cancelAction);
+ blurredBackground.appendCustomChild(container);
+ document.body.appendChild(blurredBackground.getElement());
+}
+
+export function openImportNotesWindow() {
+ const blurredBackground = new CustomDivElement('twitch-notes-blurred-background');
+ const container = new CustomDivElement('twitch-notes-center-floating-container', 'WARNING! This action might override existing data! ');
+ const inputBlock = new CustomDivElement('','Select exported twitch notes file ');
+ const input = new CustomInputElement('twitch-notes-file-input');
+ input.addAttribute('type', 'file').addAttribute('accept', 'json');
+ const settingsCloseButton = new CustomButtonElement('twitch-notes-settings-close-button', getCloseButtonSVG());
+ settingsCloseButton.setStyle({
+ position: 'absolute',
+ top: '1rem',
+ right: '1rem',
+ });
+
+ inputBlock.appendCustomChild(input);
+
+ settingsCloseButton.setClickListener(() => {
+ blurredBackground.remove();
+ })
+
+ input.setChangeListener(() => {
+ if (!input?.getElement()?.files) return;
+ // @ts-ignore
+ let file = input.getElement().files.item(0);
+ if (!file) {
+ return;
+ }
+ let reader = new FileReader();
+ reader.readAsText(file, "UTF-8");
+ reader.onload = function (evt) {
+ if (!evt?.target?.result) return;
+
+ NoteStorage.inputData = JSON.parse(evt.target.result.toString());
+ if (NoteStorage.inputData) {
+ inputBlock.setStyle({display: 'none'});
+ importNotesResult(container, blurredBackground);
+ }
+ }
+ reader.onerror = function () {
+ console.error("error reading file");
+ }
+ });
+
+ container.appendCustomChild(settingsCloseButton);
+ container.appendCustomChild(inputBlock);
+ blurredBackground.appendCustomChild(container);
+ document.body.appendChild(blurredBackground.getElement());
+}
+
+export function importNotesResult(container: CustomHTMLElement, blurredBackground: CustomHTMLElement) {
+ const willBeAdded = [];
+ let willBeOverwritten = [];
+ const noChanges = [];
+
+ for (let user of NoteStorage.inputData.users) {
+ if (NoteStorage.getNote(user)) {
+ if (NoteStorage.inputData.notes.find(note => note.user === user)?.resolved || NoteStorage.getNote(user) !== NoteStorage.inputData.notes.find(note => note.user === user)?.note) {
+ willBeOverwritten.push(user);
+ } else {
+ noChanges.push(user);
+ }
+ } else {
+ willBeAdded.push(user);
+ }
+ }
+
+ const resultsContainer = new CustomDivElement();
+
+ if (willBeOverwritten.length) {
+ const overwrittenContainer = new CustomDivElement('', 'Note conflicts found for: ');
+ overwrittenContainer.setStyle({marginTop: '1rem'});
+
+ for (let item of willBeOverwritten) {
+ const row = new CustomDivElement('', item + ' ');
+ const isResolved = NoteStorage.inputData.notes.find(note => note.user === item)?.resolved;
+ row.setStyle({
+ color: isResolved ? 'green' : 'red',
+ });
+ const resolve = new CustomAElement('', isResolved ? '[update]' : '[resolve]');
+ resolve.setStyle({
+ cursor: 'pointer'
+ });
+
+ resolve.setClickListener(() => {
+ importNotesConflictResolve(item, resultsContainer, container, blurredBackground);
+ })
+ row.appendCustomChild(resolve);
+ overwrittenContainer.appendCustomChild(row);
+ }
+ resultsContainer.appendCustomChild(overwrittenContainer);
+ }
+
+ if (willBeAdded.length) {
+ const addedContainer = new CustomDivElement('', 'Note will be added for: ' + willBeAdded.join(' '));
+ addedContainer.setStyle({
+ marginTop: '1rem',
+ color: 'white'
+ });
+ resultsContainer.appendCustomChild(addedContainer);
+ }
+
+ if (noChanges.length) {
+ const noChangesContainer = new CustomDivElement('', 'No changes for: ' + noChanges.join(' '));
+ noChangesContainer.setStyle({
+ marginTop: '1rem',
+ color: 'gray'
+ });
+ resultsContainer.appendCustomChild(noChangesContainer);
+ }
+
+ const spacer = new CustomDivElement();
+ spacer.setStyle({height: '24px'});
+ resultsContainer.appendCustomChild(spacer);
+
+ willBeOverwritten = willBeOverwritten.filter(x => !NoteStorage.inputData.notes.find(n => n.user === x)?.resolved);
+
+ const saveButton = new CustomButtonElement('twitch-notes-settings-button', 'Complete import');
+ saveButton.setStyle({})
+ if (willBeOverwritten.length > 0) saveButton.setStyle({background: 'gray'});
+ if (willBeOverwritten.length > 0) saveButton.addAttribute('disabled', 'true');
+
+ resultsContainer.appendCustomChild(saveButton)
+
+ if (willBeOverwritten.length > 0) {
+ const note = new CustomDivElement('', 'Import cannot be completed while there are unresolved conflicts ');
+ note.setStyle({
+ color: 'gray',
+ });
+ resultsContainer.appendCustomChild(note);
+ } else {
+ saveButton.setClickListener(() => {
+ for (let note of NoteStorage.inputData.notes) {
+ NoteStorage.saveNote(note.user, note.note);
+ }
+ blurredBackground.remove();
+ NoteStorage.inputData = {
+ users: [],
+ notes: [],
+ settings: {},
+ };
+ });
+ }
+
+ container.appendCustomChild(resultsContainer);
+}
+
+export function importNotesConflictResolve(
+ user: string,
+ resultsContainer: CustomHTMLElement,
+ container: CustomHTMLElement,
+ blurredBackground: CustomHTMLElement
+) {
+ const diffBlurredBackground = new CustomDivElement('twitch-notes-blurred-background');
+ const diffContainer = new CustomDivElement('twitch-notes-center-floating-container', 'Select which note to keep for ' + user + ' You can modify notes to merge them and keep updated one ');
+
+ const inputContainer = new CustomDivElement();
+ inputContainer.setStyle({
+ display: 'flex',
+ flexDirection: 'row',
+ flexWrap: 'no-wrap',
+ width: '100%',
+ });
+
+ const valueStyle = {
+ minWidth: '320px',
+ padding: '6px',
+ border: '1px solid #FFFFFF19',
+ outline: 'none',
+ };
+ const valueAttr: [string, string] = ['contentEditable', 'true'];
+
+ const localValueContainer = new CustomDivElement();
+ const pLocal = new CustomPElement('', 'Locally saved note ');
+ localValueContainer.appendCustomChild(pLocal);
+
+ const localValue = new CustomDivElement();
+ localValue.setStyle(valueStyle);
+ localValue.addAttribute(...valueAttr);
+ localValue.setId('local-value-container');
+ const localValueSave = new CustomButtonElement('twitch-notes-settings-button', 'Save this');
+ localValueSave.setClickListener(() => {
+ const val = document.getElementById('local-value-container')?.innerHTML || '';
+ NoteStorage.inputData.notes = NoteStorage.inputData.notes.map(n => n.user === user ? {
+ user: n.user,
+ note: val,
+ resolved: true
+ } : n);
+ diffBlurredBackground.remove();
+ resultsContainer.remove();
+ importNotesResult(container, blurredBackground);
+ });
+ localValueContainer.appendCustomChild(localValue);
+ localValueContainer.appendBody(' ');
+ localValueContainer.appendCustomChild(localValueSave);
+
+ const importedValueContainer = new CustomDivElement();
+ const iLocal = new CustomPElement('', 'Imported note ');
+ importedValueContainer.appendCustomChild(iLocal);
+ const importedValue = new CustomDivElement('', NoteStorage.inputData.notes.filter(n => n.user === user)[0].note || '');
+ importedValue.setId('import-value-container');
+ importedValue.setStyle(valueStyle);
+ importedValue.addAttribute(...valueAttr);
+ const importedValueSave = new CustomButtonElement('twitch-notes-settings-button', 'Save this');
+ importedValueSave.setClickListener(() => {
+ const val = document.getElementById('import-value-container')?.innerHTML || '';
+ NoteStorage.inputData.notes = NoteStorage.inputData.notes.map(n => n.user === user ? {
+ user: n.user,
+ note: val,
+ resolved: true
+ } : n);
+ diffBlurredBackground.remove();
+ resultsContainer.remove();
+ importNotesResult(container, blurredBackground);
+ });
+
+ importedValueContainer.appendCustomChild(importedValue);
+ importedValueContainer.appendBody(' ');
+ importedValueContainer.appendCustomChild(importedValueSave);
+
+
+ inputContainer.appendCustomChild(localValueContainer);
+ inputContainer.appendCustomChild(importedValueContainer);
+
+ diffContainer.appendCustomChild(inputContainer);
+ diffBlurredBackground.appendCustomChild(diffContainer);
+ document.body.appendChild(diffBlurredBackground.getElement());
+}
+
+export function createChatSettingsLine(text: string) {
+ const element = new CustomDivElement('twitch-notes-settings-option-line');
+ const button = new CustomButtonElement('twitch-notes-settings-option-line-button');
+ const buttonContainer = new CustomDivElement('twitch-notes-settings-option-line-button-container');
+ const buttonContainerContent = new CustomDivElement('twitch-notes-settings-option-line-button-container-content', text);
+
+ buttonContainer.appendCustomChild(buttonContainerContent);
+ button.appendCustomChild(buttonContainer);
+ element.appendCustomChild(button);
+ return element;
+}
+
+export function createChatSettingsSeparator() {
+ return new CustomDivElement('twitch-note-settings-separator');
+}
+
+export function openTwitchNote(username: string) {
+ if (NoteContainers.containers[username]) return;
+ const container = new CustomDivElement('twitch-note-container');
+ container.setStyle({
+ left: Mouse.mousePosition.x + 'px',
+ top: Mouse.mousePosition.y + 10 + 'px'
+ });
+
+ const header = new CustomDivElement('twitch-note-header');
+ header.setMouseDownListener(() => {
+ NoteContainers.activeContainer = username;
+ Mouse.isMouseDown = true;
+ });
+
+ const closeButton = new CustomSpanElement('twitch-note-close-button', getCloseButtonSVG());
+ closeButton.setClickListener(() => {
+ NoteContainers.removeContainer(username);
+ });
+
+ const title = new CustomSpanElement('twitch-note-title', username);
+ const content = new CustomDivElement('twitch-note-content', NoteStorage.getNote(username));
+ content.addAttribute('contentEditable', 'true');
+
+ const saveButton = new CustomDivElement('twitch-note-save-button', 'SAVE');
+ saveButton.setClickListener(() => {
+ NoteStorage.saveNote(username, content.getElement().innerHTML);
+ NoteContainers.removeContainer(username);
+ });
+
+ header.appendCustomChild(closeButton);
+ header.appendCustomChild(title);
+ container.appendCustomChild(header);
+ container.appendCustomChild(content);
+ container.appendCustomChild(saveButton);
+ document.body.appendChild(container.getElement());
+ NoteContainers.addContainer(username, container);
+}
+
+export function getCloseButtonSVG() {
+ return `
+
+
+
+ `;
+}
\ No newline at end of file
diff --git a/src/Mouse.ts b/src/Mouse.ts
new file mode 100644
index 0000000..7a11635
--- /dev/null
+++ b/src/Mouse.ts
@@ -0,0 +1,43 @@
+import NoteContainers from "./NoteContainers";
+
+
+
+export default class Mouse {
+
+ static isMouseDown = false;
+ static mousePosition = {x: 0, y: 0};
+ static lastMousePosition = {x: 0, y: 0};
+ static listenersSetUp = false;
+
+ static setupListeners() {
+ if (!Mouse.listenersSetUp) {
+ document.addEventListener("mousemove", Mouse.handleMouseMove);
+ document.addEventListener("mouseup", Mouse.handleMouseUp);
+ Mouse.listenersSetUp = true;
+ }
+ }
+
+ static handleMouseUp() {
+ NoteContainers.activeContainer = '';
+ Mouse.isMouseDown = false;
+ }
+
+ static updateMousePosition(x: number, y: number) {
+ Mouse.lastMousePosition = {...this.mousePosition};
+ Mouse.mousePosition = {x, y};
+ }
+
+ static handleMouseMove(event: MouseEvent) {
+ if (Mouse.isMouseDown) {
+ if (NoteContainers.activeContainer) {
+ const dx = Mouse.mousePosition.x - Mouse.lastMousePosition.x;
+ const dy = Mouse.mousePosition.y - Mouse.lastMousePosition.y;
+ NoteContainers.containers[NoteContainers.activeContainer].setStyle({
+ top: parseInt(NoteContainers.containers[NoteContainers.activeContainer].getElement().style.top) + dy + "px",
+ left: parseInt(NoteContainers.containers[NoteContainers.activeContainer].getElement().style.left) + dx + "px",
+ });
+ }
+ }
+ Mouse.updateMousePosition(event.clientX, event.clientY);
+ }
+};
\ No newline at end of file
diff --git a/src/NoteContainers.ts b/src/NoteContainers.ts
new file mode 100644
index 0000000..edb8791
--- /dev/null
+++ b/src/NoteContainers.ts
@@ -0,0 +1,17 @@
+import {CustomHTMLElement} from "./CustomHTMLElement";
+
+const containers: {[key: string]: CustomHTMLElement} = {};
+
+export default {
+ activeContainer: '',
+ containers,
+ addContainer(username: string, container: CustomHTMLElement) {
+ if (this.containers[username]) return;
+ this.containers[username] = container;
+ },
+ removeContainer(username: string) {
+ if (!this.containers[username]) return;
+ this.containers[username].remove();
+ delete this.containers[username];
+ }
+}
\ No newline at end of file
diff --git a/src/NoteStorage.ts b/src/NoteStorage.ts
new file mode 100644
index 0000000..616734a
--- /dev/null
+++ b/src/NoteStorage.ts
@@ -0,0 +1,81 @@
+import {LS_Prefix, LS_UserList} from './const';
+
+export default class NoteStorage {
+
+ static inputData: {
+ users: string[],
+ notes: {
+ user: string,
+ note: string,
+ resolved?: boolean,
+ }[],
+ settings: any,
+ } = {
+ users: [],
+ notes: [],
+ settings: {},
+ };
+
+ static getSavedUserList(): string[] {
+ return JSON.parse(localStorage.getItem(LS_UserList) || "[]");
+ }
+
+ static getNote(username: string): string {
+ return localStorage.getItem(LS_Prefix + username) || "";
+ }
+
+ static addUserToSavedList(username: string) {
+ let userList = this.getSavedUserList();
+
+ if (!userList.includes(username)) {
+ userList.push(username);
+ localStorage.setItem(LS_UserList, JSON.stringify(userList));
+ }
+ }
+
+ static removeUserFromSavedList(username: string) {
+ let userList = this.getSavedUserList();
+ userList = userList.filter(u => u !== username);
+ if (userList.length) {
+ localStorage.setItem(LS_UserList, JSON.stringify(userList));
+ } else {
+ localStorage.removeItem(LS_UserList);
+ }
+ }
+
+ static saveNote(username: string, note: string) {
+ this.addUserToSavedList(username);
+ localStorage.setItem(LS_Prefix + username, note);
+ }
+
+ static deleteNote(username: string) {
+ this.removeUserFromSavedList(username);
+ localStorage.removeItem(LS_Prefix + username);
+ }
+
+ static exportNotes() {
+ const dataObject = {
+ users: this.getSavedUserList(),
+ notes: this.getSavedUserList().map(user => {
+ return {user: user, note: this.getNote(user)};
+ }),
+ settings: {},
+ };
+ const data = JSON.stringify(dataObject)
+
+ const blob = new Blob([data], {type: 'text/json'});
+ const elem = window.document.createElement('a');
+ elem.href = window.URL.createObjectURL(blob);
+ elem.download = 'twitch-notes-' + (Date.now()) + '.json';
+ document.body.appendChild(elem);
+ elem.click();
+ document.body.removeChild(elem);
+ }
+
+ static clearData() {
+ const users = this.getSavedUserList();
+ for(let user of users) {
+ this.deleteNote(user);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/TwitchNote.ts b/src/TwitchNote.ts
index 8dd99be..8c3bd22 100644
--- a/src/TwitchNote.ts
+++ b/src/TwitchNote.ts
@@ -1,3 +1,12 @@
+import {
+ CustomButtonElement,
+ CustomDivElement,
+ CustomImgElement,
+ CustomSpanElement
+} from "./CustomHTMLElement";
+import {ELEMENT_ButtonContainer} from "./const";
+import {openTwitchNote, toggleSettingsList} from "./HTMLTemplates";
+
export default class TwitchNote {
observer: MutationObserver | null = null;
@@ -8,10 +17,12 @@ export default class TwitchNote {
init(chatContainerNode: Element) {
this.chatContainerNode = chatContainerNode;
- console.log('CHAT CONTAINER INITIATED');
this.startObserver(chatContainerNode);
// add Notes management button
this.addNotesButton();
+ setTimeout(() => {
+ this.addUserNoteBadges();
+ }, 1000);
}
startObserver(targetNode: Element) {
@@ -42,8 +53,63 @@ export default class TwitchNote {
}
addNotesButton() {
+ const buttonContainer = document.querySelector(`.${ELEMENT_ButtonContainer}`);
+ if (!buttonContainer) {
+ setTimeout(() => this.addNotesButton(), 100);
+ return;
+ }
+
+ const container1 = new CustomDivElement();
+ container1.setStyle({marginLeft: "0.5rem !important"});
+ const container2 = new CustomDivElement();
+ container1.setStyle({display: "inline-flex !important"});
+ const settingsButton = new CustomButtonElement('twitch-notes-settings-button', 'Notes');
+
+ settingsButton.setClickListener(() => {
+ toggleSettingsList();
+ });
+
+ container2.appendCustomChild(settingsButton);
+ container1.appendCustomChild(container2);
+
+ if (buttonContainer.lastChild) {
+ buttonContainer.lastChild.insertBefore(
+ container1.getElement(),
+ buttonContainer.lastChild.lastChild
+ );
+ } else {
+ buttonContainer.appendChild(container1.getElement());
+ }
}
addUserNoteBadges() {
+ let nodes = document.getElementsByClassName("chat-line__message");
+ for (let i = 0; i < nodes.length; i++) {
+ const node = nodes.item(i);
+ if (!node) continue;
+ const n = node.querySelector(".chat-author__display-name");
+ if (!n) continue;
+ const usernameContainer = node.querySelector(".chat-line__username-container");
+ if (!usernameContainer) continue;
+ const username = n.getAttribute('data-a-user');
+ if (username) {
+ let noteButton = usernameContainer.querySelector(".twitch-note");
+ if (!noteButton) {
+ const twitchNote = new CustomSpanElement('twitch-note');
+ twitchNote.setStyle({cursor: 'pointer'});
+ twitchNote.setClickListener(() => {
+ openTwitchNote(username);
+ });
+ const img = new CustomImgElement();
+ img.setStyle({height: '18px', paddingRight: '4px'});
+ img.addAttribute('src', 'https://cdn.rdarius.lt/icons/32-id-card.png');
+ twitchNote.appendCustomChild(img);
+ usernameContainer.insertBefore(
+ twitchNote.getElement(),
+ usernameContainer.firstChild
+ );
+ }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/const.ts b/src/const.ts
new file mode 100644
index 0000000..6353f9a
--- /dev/null
+++ b/src/const.ts
@@ -0,0 +1,3 @@
+export const LS_UserList = "twitch-note-all-users-list";
+export const LS_Prefix = "twitch-note-";
+export const ELEMENT_ButtonContainer = "chat-input__buttons-container";
\ No newline at end of file
diff --git a/src/index.ts b/src/index.ts
index d97d26a..b67eba6 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,5 +1,6 @@
import './styles/main.scss';
import TwitchNote from "./TwitchNote";
+import Mouse from "./Mouse";
const ChatContainerClass = 'chat-scrollable-area__message-container';
const twitchNote = new TwitchNote();
@@ -24,6 +25,7 @@ function init() {
if (!chatExists) {
// chat container appeared
twitchNote.init(targetNode);
+ Mouse.setupListeners();
}
chatExists = true;
} else {
diff --git a/src/types.ts b/src/types.ts
new file mode 100644
index 0000000..314197a
--- /dev/null
+++ b/src/types.ts
@@ -0,0 +1,10 @@
+import {CustomHTMLElement} from "./CustomHTMLElement";
+
+export type CustomHTMLElementConstructor = {
+ class?: string | string[],
+ id?: string,
+ css?: Partial,
+ attributes?: {[key: string]: string},
+ body?: string | HTMLElement,
+ customBody?: CustomHTMLElement,
+}
\ No newline at end of file