/**********/ /* EVENTS */ /**********/ /* Triggered when a click is received by the mode selector switch, or by either of the two labels (“Blacklist” and “Whitelist”). */ function modeSelectorInputReceived() { toggleModeSelectorState(); // Activate the “Reset” and “Save” buttons, since changes have been made. setButtonsActive(true); } /***********/ /* HELPERS */ /***********/ /* Toggles all UI state associated with mode selection. Called on input to the mode selector (see modeSelectorInputReceived()), OR when changes are reset (see resetChanges()). */ function toggleModeSelectorState(newMode) { /* Update state of the checkbox itself, and the container div. */ let container = document.querySelector(".mode-select-container"); let checkbox = document.querySelector("input#whitelist-mode"); newMode = newMode || (container.classList.contains("whitelist") ? "blacklist" : "whitelist"); container.classList.toggle("whitelist", (newMode == "whitelist")); checkbox.checked = (newMode == "whitelist"); /* Enable both spans (the “Blacklist” and “Whitelist” labels, then disable just the label associated with the now-enabled mode. */ container.querySelectorAll("span").forEach(span => { span.classList.toggle("disabled", false); }); document.querySelector(`.${newMode}-mode-label`).classList.toggle("disabled", true); /* In whitelist mode, the matching patterns textarea must be disabled, since the matching patterns list is treated as containing only the single pattern “.*”. An overlay is shown, to indicate this. */ document.querySelector("div#matchingPatterns").classList.toggle("disabled", (newMode == "whitelist")); document.querySelector("div#matchingPatterns textarea").disabled = (newMode == "whitelist"); } /* Saves the entered state in storage. De-activate the “Reset” and “Save” buttons. */ function saveChanges() { let matchingPatterns = document.querySelector("#matchingPatterns textarea").value; let exclusionPatterns = document.querySelector("#exclusionPatterns textarea").value; let mode = document.querySelector("input#whitelist-mode").checked ? "whitelist" : "blacklist"; chrome.storage.sync.set({ "matchingPatterns": matchingPatterns, "exclusionPatterns": exclusionPatterns, "mode": mode }, () => { setButtonsActive(false); }); } /* Retrieves the saved state from storage and reset the UI to reflect it, discarding the user’s changes. */ function resetChanges(callback) { chrome.storage.sync.get([ "matchingPatterns", "exclusionPatterns", "mode" ], (result) => { AKS.matchingPatterns = result.matchingPatterns || ""; AKS.exclusionPatterns = result.exclusionPatterns || "" AKS.mode = result.mode || "blacklist"; document.querySelector("#matchingPatterns textarea").value = AKS.matchingPatterns; document.querySelector("#exclusionPatterns textarea").value = AKS.exclusionPatterns; toggleModeSelectorState(AKS.mode); // De-activate the “Reset” and “Save” buttons. setButtonsActive(false); // Expand all textareas. document.querySelectorAll("textarea").forEach(textarea => { expandTextarea(textarea); }); // Trigger provided callback, if any. if (callback) callback(); }); } /* Activates or de-activates the “Reset” and “Save” buttons. Called when changes are made (to activate) or when changes are saved or reset (to de-activate). */ function setButtonsActive(active) { document.querySelectorAll("button").forEach(button => { button.disabled = !active; }); } /* Expands a textarea to show all its contents (up to a maximum height which is set via CSS). */ function expandTextarea(textarea) { let totalBorderHeight = 2; if (textarea.clientHeight == textarea.scrollHeight + totalBorderHeight) return; requestAnimationFrame(() => { textarea.style.height = 'auto'; textarea.style.height = textarea.scrollHeight + totalBorderHeight + 'px'; }); } /******************/ /* INITIALIZATION */ /******************/ function initialize() { window.AKS = { }; /* Retrieve saved settings from storage, and set all UI elements to their proper state. Then, focus the first active textarea. */ resetChanges(() => { let divToFocus = (AKS.mode == "whitelist") ? "#exclusionPatterns" : "#matchingPatterns"; document.querySelector(`${divToFocus} textarea`).focus(); }); // Listeners for Reset and Save buttons. document.querySelectorAll("button").forEach(button => { button.addActivateEvent((event) => { event.target.blur(); if (button.classList.contains("save-button")) saveChanges(); else resetChanges(); }); }); // Listeners to activate Reset/Save buttons when textareas get input. document.querySelectorAll("textarea").forEach(textarea => { textarea.addEventListener("input", (event) => { setButtonsActive(true); }); }); // Listeners to auto-expand textareas on input. document.querySelectorAll("textarea").forEach(textarea => { textarea.addEventListener("input", (event) => { expandTextarea(textarea); }); }); // Listeners for mode select switch. document.querySelector("input[type='checkbox']").addEventListener("change", (event) => { modeSelectorInputReceived(); }); document.querySelectorAll(".mode-select-container span").forEach(span => { span.addActivateEvent((event) => { modeSelectorInputReceived(); }); }); } initialize();