From 1428aa9a26e2d25a5c20f5af003f3c850fd9f313 Mon Sep 17 00:00:00 2001 From: sto Date: Sat, 12 Apr 2025 14:40:20 +0200 Subject: [PATCH] Initial app --- icon.png | Bin 0 -> 4430 bytes manifest.json | 19 ++++++++ popup.html | 80 +++++++++++++++++++++++++++++++++ popup.js | 110 +++++++++++++++++++++++++++++++++++++++++++++ scripts/content.js | 73 ++++++++++++++++++++++++++++++ 5 files changed, 282 insertions(+) create mode 100644 icon.png create mode 100644 manifest.json create mode 100644 popup.html create mode 100644 popup.js create mode 100644 scripts/content.js diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7afa88054dbd81d5cb4f74958f784e3785a40b21 GIT binary patch literal 4430 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 + + + +

Puzzle contest

+
+
0:00:00
+ + + + +
+
+
+
+ + diff --git a/popup.js b/popup.js new file mode 100644 index 0000000..d5a43b2 --- /dev/null +++ b/popup.js @@ -0,0 +1,110 @@ +let startTime = 0; +let endTime = 0; +let messagesCount = 0; +let showMessages = false; + +async function init() { + const data = await chrome.storage.local.get(['startTime', 'endTime']); + if (data?.endTime > 0) { + startTime = data.startTime; + endTime = data.endTime; + document.getElementById('reset-button').style.display = 'block'; + } else if (data?.startTime > 0) { + startTime = data.startTime; + document.getElementById('start-button').style.display = 'none'; + document.getElementById('end-button').style.display = 'block'; + } + const startButton = document.getElementById('start-button'); + startButton.onclick = startContest; + const endButton = document.getElementById('end-button'); + endButton.onclick = endContest; + const resetButton = document.getElementById('reset-button'); + resetButton.onclick = resetData; + const messagesButton = document.getElementById('messages-button'); + messagesButton.onclick = switchMessages; + update(); +} + +async function fetchMessages() { + const data = await chrome.storage.local.get('messages'); + if (data.messages && data.messages.length > messagesCount) { + const messagesEl = document.getElementById('messages'); + let html = messagesEl.innerHTML; + for (let i = messagesCount; i < data.messages.length; i++) { + const {displayTime, completionTime, name, text} = data.messages[i]; + html += `
${displayTime}${name}${text}
` + } + messagesEl.innerHTML = html; + messagesCount = data.messages.length; + } +} + +function sendMessage(message, callback) { + chrome.tabs.query({active: true, currentWindow: true}, tabs => { + tabs.forEach(tab => chrome.tabs.sendMessage(tab.id, message, callback)); + }); +} + +function startContest() { + startTime = Date.now(); + endTime = 0; + chrome.storage.local.set({ startTime: startTime, endTime: 0, messages: [] }); + document.getElementById('start-button').style.display = 'none'; + document.getElementById('end-button').style.display = 'block'; + document.getElementById('reset-button').style.display = 'none'; + document.getElementById('messages').innerHTML = ''; + if (showMessages) switchMessages(); +} + +function endContest() { + endTime = Date.now(); + chrome.storage.local.set({ endTime: endTime }); + document.getElementById('start-button').style.display = 'block'; + document.getElementById('end-button').style.display = 'none'; + document.getElementById('reset-button').style.display = 'block'; +} + +function resetData() { + chrome.storage.local.set({ startTime: 0, endTime: 0, messages: [] }); + startTime = 0; + endTime = 0; + document.getElementById('time').innerHTML = '0:00:00'; + document.getElementById('reset-button').style.display = 'none'; + document.getElementById('messages').innerHTML = ''; + if (showMessages) switchMessages(); +} + +function switchMessages() { + const messagesEl = document.getElementById('messages'); + const messagesButtonEl = document.getElementById('messages-button'); + if (showMessages) { + messagesEl.style.display = 'none'; + messagesButtonEl.innerHTML = 'show messages'; + } else { + messagesEl.style.display = 'block'; + messagesButtonEl.innerHTML = 'hide messages'; + } + showMessages = !showMessages; +} + +function dis(n) { + if (n < 10) return "0" + n; + return n.toString(); +} + +function update() { + // Update the display time in the popup. + let displayTime = '0:00:00'; + if (startTime != 0) { + let rawTime = Math.floor((Date.now() - startTime) / 1000); + if (endTime > 0) rawTime = Math.floor((endTime - startTime) / 1000); + displayTime = `${Math.floor(rawTime / 3600)}:${dis(Math.floor((rawTime % 3600) / 60))}:${dis(rawTime % 60)}`; + } + document.getElementById('time').innerHTML = displayTime; + setTimeout(update, 1000); + + // Fetch and save new messages. + fetchMessages(); +} + +setTimeout(init, 20); diff --git a/scripts/content.js b/scripts/content.js new file mode 100644 index 0000000..5c0937f --- /dev/null +++ b/scripts/content.js @@ -0,0 +1,73 @@ +const seenMessages = new Set(); +const seenDataAnnounceMessages = new Set(); +let awaitingTimes = []; +let messages = []; +let startTime = 0; + +async function init() { + const data = await chrome.storage.local.get(['startTime', 'messages']); + if (data?.startTime > 0) { + startTime = data.startTime; + } + if (data.messages) { + messages = data.messages; + } + checkNewMessages(); + update(); +} + +function dis(n) { + if (n < 10) return "0" + n; + return n.toString(); +} + +function displayTime(rawTime) { + if (rawTime < 3600) return `${dis(Math.floor((rawTime % 3600) / 60))}:${dis(rawTime % 60)}`; + return `${Math.floor(rawTime / 3600)}:${dis(Math.floor((rawTime % 3600) / 60))}:${dis(rawTime % 60)}`; +} + +function checkNewMessages() { + // Check announce messages and log their appearing times. + const announceMessageElements = document.querySelectorAll("div[data-announce-message]"); + for (let e of announceMessageElements) { + if (seenDataAnnounceMessages.has(e)) continue; + seenDataAnnounceMessages.add(e); + const completionTime = Math.floor((Date.now() - startTime) / 1000); + awaitingTimes.push(completionTime); + } + + // Check in-call messages. + const messageElements = document.querySelectorAll("div[data-message-id] div[jscontroller]"); + for (let e of messageElements) { + if (seenMessages.has(e)) continue; + seenMessages.add(e); + let completionTime = Math.floor((Date.now() - startTime) / 1000); + + // If awaiting times are available, use them first (i.e. the messages section just got opened). + if (awaitingTimes.length) completionTime = awaitingTimes.shift(); + const messageNode = e.parentNode.parentNode.parentNode.parentNode; + const rootNode = messageNode.parentNode.parentNode; + const name = rootNode.firstChild.firstChild.innerHTML; + const dTime = displayTime(completionTime); + messages.push({completionTime: completionTime, name: name, text: e.innerHTML, displayTime: dTime}); + e.innerHTML = `${dTime}${e.innerHTML}` + } + chrome.storage.local.set({ messages: messages }); + + // If awaiting messages are still present, but the messages section is opened with no new messages, + // delete all awaiting messages (that should never happen though, there was an error somewhere). + if (messageElements.length && awaitingTimes.length) awaitingTimes = []; + + setTimeout(checkNewMessages, 100); +} + +async function update() { + const data = await chrome.storage.local.get(['startTime', 'endTime']); + if (data?.startTime >= 0 && data.startTime != startTime) { + startTime = data.startTime; + messages = []; + } + setTimeout(update, 2000); +} + +setTimeout(init, 20);