From 56a268a32ea646693b53e30a585f7b9de50cbc00 Mon Sep 17 00:00:00 2001 From: Rio Martinez Date: Thu, 4 Mar 2021 23:22:13 -0500 Subject: [PATCH] add translate language and enable toggle settings --- content.js | 78 ++++++++++++++++++++++++++++++++++------------ manifest.json | 8 +++-- options.css | 40 ++++++++++++++++++++++++ options.html | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++ options.js | 43 ++++++++++++++++++++++++++ 5 files changed, 233 insertions(+), 21 deletions(-) create mode 100644 options.css create mode 100644 options.html create mode 100644 options.js diff --git a/content.js b/content.js index 6f06f6b..33edb37 100644 --- a/content.js +++ b/content.js @@ -1,9 +1,10 @@ +let globalTL = 'en'; + // Called for each new caption node mounted to the DOM function handleCaptionMount(node) { const text = node.innerText; - // Handle translation request within service-worker to alleviate CORS/security conflicts - const req = { action: 'translate', payload: { text, source_lang: 'no', target_lang: 'en' } }; + const req = { action: 'translate', payload: { text, source_lang: 'no', target_lang: globalTL } }; chrome.extension.sendRequest(req, ({ error, response }) => { if (!error) { node.setAttribute('data-translation', response); @@ -13,23 +14,62 @@ function handleCaptionMount(node) { }); } -// Listen for captions elements mounting -const captionsContainerQuery = '.ludo-captions'; -const containerObserver = new MutationObserver((mutations) => { - mutations.forEach((mutation) => { - if (!mutation.addedNodes) return; - Array.from(mutation.addedNodes).forEach((node) => { - const parent = node.parentElement; - const isCaption = parent && parent.matches(captionsContainerQuery); - if (isCaption) { - handleCaptionMount(node); - } +const CaptionObserver = (function (handler) { + // Listen for captions elements mounting + const captionsContainerQuery = '.ludo-captions'; + const captionsObserver = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (!mutation.addedNodes) return; + Array.from(mutation.addedNodes).forEach((node) => { + const parent = node.parentElement; + const isCaption = parent && parent.matches(captionsContainerQuery); + if (isCaption) { + handler(node); + } + }); }); }); -}); -containerObserver.observe(document.body, { - childList: true, - subtree: true, - attributes: false, - characterData: false, + // Return API interface + return { + enable() { + captionsObserver.observe(document.body, { + childList: true, + subtree: true, + attributes: false, + characterData: false, + }); + }, + disable() { + captionsObserver.disconnect(); + }, + }; +})(handleCaptionMount); + +function handleSettingsUpdate({ en, tl }) { + en ? CaptionObserver.enable() : CaptionObserver.disable(); + globalTL = tl; +} + +function handleSettingsChanges(changes) { + chrome.storage.sync.get((settings) => { + // Merge changes with current settings + Object.entries(changes).forEach(([setting, { newValue: value }]) => { + settings[setting] = value; + }); + // Handle changes + handleSettingsUpdate({ + en: settings['settings-en'], + tl: settings['settings-tl'], + }); + }); +} + +// Load current settings on extension mount +handleSettingsChanges({}); + +// Listen for extension settings updates +chrome.storage.onChanged.addListener(function (changes, area) { + if (area == 'sync') { + handleSettingsChanges(changes); + } }); diff --git a/manifest.json b/manifest.json index 9469d31..f3b8eeb 100644 --- a/manifest.json +++ b/manifest.json @@ -15,7 +15,11 @@ "css": ["styles.css"] } ], + "options_page": "options.html", "browser_action": { - "default_icon": {} - } + "default_icon": {}, + "default_popup": "options.html" + }, + "permissions": ["storage"], + "content_security_policy": "script-src 'self'" } diff --git a/options.css b/options.css new file mode 100644 index 0000000..0a7d1b7 --- /dev/null +++ b/options.css @@ -0,0 +1,40 @@ +* { + width: 100%; + box-sizing: border-box; + padding: 0; + margin: 0; +} + +html { + position: relative; + margin: 0 auto; + width: 350px; + height: 100%; + background-color: rgb(54, 50, 77); +} + +body { + width: 100%; + height: min-content; + padding: 25px; +} + +h1 { + font-weight: 400; + color: white; + margin-bottom: 15px; +} + +label { + width: max-content; + color: #e5cfff; + font-size: 15px; + margin-bottom: 8px; + white-space: nowrap; + margin-right: 15px; +} + +.field-container { + display: flex; + margin-bottom: 15px; +} diff --git a/options.html b/options.html new file mode 100644 index 0000000..35afed7 --- /dev/null +++ b/options.html @@ -0,0 +1,85 @@ + + + + NRK TV Dual Subtitles Preferences + + + +
+
+ + +
+
+ + +
+ +
+ + + diff --git a/options.js b/options.js new file mode 100644 index 0000000..ac8c1ff --- /dev/null +++ b/options.js @@ -0,0 +1,43 @@ +const settingsForm = document.getElementById('settings-form'); + +function forEachSetting(cb) { + Array.from(settingsForm.childNodes).forEach((fieldWrapper) => { + const children = Array.from(fieldWrapper.childNodes).filter((n) => n.nodeName !== '#text'); + const field = children[1]; + if (field) cb(field); + }); +} + +// Save settings +settingsForm.addEventListener('submit', function handleSaveSettings(e) { + e.preventDefault(); + + const newSettings = {}; + forEachSetting((node) => { + console.log(node); + if (node.type === 'checkbox') { + newSettings[node.id] = node.checked; + } else { + newSettings[node.id] = node.value; + } + }); + chrome.storage.sync.set(newSettings, function () {}); +}); + +// Restore settings +document.addEventListener('DOMContentLoaded', function () { + chrome.storage.sync.get((settings) => { + forEachSetting((node) => { + const key = node.id; + const value = settings[key]; + console.log({ key, value }); + if (value) { + if (node.type === 'checkbox') { + node.checked = value; + } else { + node.value = value; + } + } + }); + }); +});