You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

136 lines
4.8 KiB

function setChatWidth(width) {
document.querySelector('body').style.setProperty("--chat-width", width + 'px');
}
function setHighlightColor(color) {
document.querySelector('body').style.setProperty("--highlight-color", color);
}
chrome.storage.onChanged.addListener((changes, areaName) => {
if ('chatWidth' in changes) {
setChatWidth(changes.chatWidth.newValue);
}
if ('highlightColor' in changes) {
setHighlightColor(changes.highlightColor.newValue);
}
});
chrome.storage.sync.get(null, (settings) => {
if ('chatWidth' in settings) {
setChatWidth(settings.chatWidth);
}
if ('highlightColor' in settings) {
setHighlightColor(settings.highlightColor);
}
});
class TabCompletion {
constructor() {
this.ownUsernameRegExp = null;
this.usernames = [];
}
setOwnUsername(username) {
this.ownUsernameRegExp = new RegExp('@?' + username.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'), 'gi');
}
addUsername(username) {
if (!this.usernames.includes(username)) {
this.usernames.push(username);
}
}
complete(input, start, symbol, choices) {
let partial = input.value.substring(start + 1, input.selectionStart).trim();
if (partial === this[`suggestion_${symbol}`]) {
partial = this[`partial_${symbol}`];
} else {
this[`partial_${symbol}`] = partial;
}
const possibilities = choices.filter((choice) => choice.toLowerCase().startsWith(partial.toLowerCase()));
if (this[`completionIndex_${symbol}`] === undefined || ++this[`completionIndex_${symbol}`] >= possibilities.length) {
this[`completionIndex_${symbol}`] = 0;
}
if (possibilities.length > 0) {
this[`suggestion_${symbol}`] = possibilities[this[`completionIndex_${symbol}`]].trim();
input.value = input.value.substring(0, start + 1) + this[`suggestion_${symbol}`] +
' ' + input.value.substring(input.selectionStart);
input.selectionStart = start + this[`suggestion_${symbol}`].length + 2;
input.selectionEnd = input.selectionStart;
}
}
attempt(input) {
const at = input.value.lastIndexOf('@', input.selectionStart - 1);
let colon = input.value.lastIndexOf(':', input.selectionStart - 1);
// ignore colon at the end of emoji shortname
if (colon !== -1) {
const match = emojis.shortnameRegExp.exec(input.value);
if (match !== null) {
colon = match.index;
}
}
if (at !== -1 && at > colon) {
this.complete(input, at, '@', this.usernames);
} else if (colon !== -1 && colon > at) {
this.complete(input, colon, ':', emojis.shortnames.map((s => s + ':')));
} else {
return false;
}
return true;
}
}
const tabCompletion = new TabCompletion();
(new MutationObserver((mutationsList, observer) => {
for (let mutation of mutationsList) {
for (let node of mutation.addedNodes) {
if (node.nodeType === 1 && (username = node.querySelector('span.user-name')) !== null) {
(new MutationObserver((mutationsList, observer) => {
for (let mutation of mutationsList) {
tabCompletion.setOwnUsername(mutation.target.data.trim());
}
})).observe(username, {characterData: true, subtree: true});
}
if (node.nodeType === 1 && (chatInput = node.querySelector('textarea#input-chat')) !== null) {
chatInput.addEventListener('keydown', (event) => {
if (event.key == 'Tab' && tabCompletion.attempt(event.currentTarget)) {
event.preventDefault();
}
});
}
if (node.nodeType === 1 && (username = node.querySelector('a.name')) !== null) {
if (!username.text.trim().startsWith('{')) {
tabCompletion.addUsername(username.text.trim());
}
}
if (node.nodeType === 1 && (messageContent = node.querySelector('div.message-content-main')) !== null) {
if (tabCompletion.ownUsernameRegExp !== null) {
const updated = messageContent.innerHTML.replace(tabCompletion.ownUsernameRegExp,
'<span style="font-weight: bold">$&</span>');
if (messageContent.innerHTML !== updated) {
messageContent.innerHTML = updated;
messageContent.parentNode.classList.add('highlighted');
}
}
}
}
}
})).observe(document, {childList: true, subtree: true});