add nudge to settings

This commit is contained in:
Josh Patra
2025-05-19 12:54:35 -04:00
parent 278baa1a05
commit 2d8a4fc25f
5 changed files with 352 additions and 399 deletions

View File

@@ -22,13 +22,17 @@ var tcDefaults = {
twitter.com
imgur.com
teams.microsoft.com
`.replace(regStrip, "")
`.replace(regStrip, ""),
// ADDED: Nudge defaults
enableSubtitleNudge: true,
subtitleNudgeInterval: 25,
subtitleNudgeAmount: 0.001
};
var keyBindings = [];
var keyBindings = []; // This is populated during save/restore
var keyCodeAliases = {
0: "null",
/* ... same as your original ... */ 0: "null",
null: "null",
undefined: "null",
32: "Space",
@@ -74,85 +78,55 @@ var keyCodeAliases = {
220: "\\",
221: "]",
222: "'",
59: ";",
61: "+",
173: "-",
59: ";",
61: "+",
173: "-"
};
function recordKeyPress(e) {
/* ... same as your original ... */
if (
(e.keyCode >= 48 && e.keyCode <= 57) || // Numbers 0-9
(e.keyCode >= 65 && e.keyCode <= 90) || // Letters A-Z
keyCodeAliases[e.keyCode] // Other character keys
(e.keyCode >= 48 && e.keyCode <= 57) ||
(e.keyCode >= 65 && e.keyCode <= 90) ||
keyCodeAliases[e.keyCode]
) {
e.target.value =
keyCodeAliases[e.keyCode] || String.fromCharCode(e.keyCode);
e.target.keyCode = e.keyCode;
e.preventDefault();
e.stopPropagation();
} else if (e.keyCode === 8) {
// Clear input when backspace pressed
e.target.value = "";
} else if (e.keyCode === 27) {
// When esc clicked, clear input
e.target.value = "null";
e.target.keyCode = null;
}
}
function inputFilterNumbersOnly(e) {
/* ... same as your original ... */
var char = String.fromCharCode(e.keyCode);
if (!/[\d\.]$/.test(char) || !/^\d+(\.\d*)?$/.test(e.target.value + char)) {
e.preventDefault();
e.stopPropagation();
}
}
function inputFocus(e) {
e.target.value = "";
/* ... same as your original ... */ e.target.value = "";
}
function inputBlur(e) {
e.target.value =
/* ... same as your original ... */ e.target.value =
keyCodeAliases[e.target.keyCode] || String.fromCharCode(e.target.keyCode);
}
function updateShortcutInputText(inputId, keyCode) {
document.getElementById(inputId).value =
keyCodeAliases[keyCode] || String.fromCharCode(keyCode);
document.getElementById(inputId).keyCode = keyCode;
}
// function updateShortcutInputText(inputId, keyCode) { /* ... same as your original ... */ } // Not directly used in provided options.js logic flow
function updateCustomShortcutInputText(inputItem, keyCode) {
inputItem.value = keyCodeAliases[keyCode] || String.fromCharCode(keyCode);
/* ... same as your original ... */ inputItem.value =
keyCodeAliases[keyCode] || String.fromCharCode(keyCode);
inputItem.keyCode = keyCode;
}
// List of custom actions for which customValue should be disabled
var customActionsNoValues = ["pause", "muted", "mark", "jump", "display"];
var customActionsNoValues = ["pause", "muted", "mark", "jump", "display"]; // Original
function add_shortcut() {
var html = `<select class="customDo">
<option value="slower">Decrease speed</option>
<option value="faster">Increase speed</option>
<option value="rewind">Rewind</option>
<option value="advance">Advance</option>
<option value="reset">Reset speed</option>
<option value="fast">Preferred speed</option>
<option value="muted">Mute</option>
<option value="pause">Pause</option>
<option value="mark">Set marker</option>
<option value="jump">Jump to marker</option>
<option value="display">Show/hide controller</option>
</select>
<input class="customKey" type="text" placeholder="press a key"/>
<input class="customValue" type="text" placeholder="value (0.10)"/>
<select class="customForce">
<option value="false">Do not disable website key bindings</option>
<option value="true">Disable website key bindings</option>
</select>
<button class="removeParent">X</button>`;
/* ... same as your original ... */
var html = `<select class="customDo"><option value="slower">Decrease speed</option><option value="faster">Increase speed</option><option value="rewind">Rewind</option><option value="advance">Advance</option><option value="reset">Reset speed</option><option value="fast">Preferred speed</option><option value="muted">Mute</option><option value="pause">Pause</option><option value="mark">Set marker</option><option value="jump">Jump to marker</option><option value="display">Show/hide controller</option></select><input class="customKey" type="text" placeholder="press a key"/><input class="customValue" type="text" placeholder="value (0.10)"/><select class="customForce"><option value="false">Do not disable website key bindings</option><option value="true">Disable website key bindings</option></select><button class="removeParent">X</button>`;
var div = document.createElement("div");
div.setAttribute("class", "row customs");
div.innerHTML = html;
@@ -162,14 +136,13 @@ function add_shortcut() {
customs_element.children[customs_element.childElementCount - 1]
);
}
function createKeyBindings(item) {
/* ... same as your original ... */
const action = item.querySelector(".customDo").value;
const key = item.querySelector(".customKey").keyCode;
const value = Number(item.querySelector(".customValue").value);
const force = item.querySelector(".customForce").value;
const predefined = !!item.id; //item.id ? true : false;
const predefined = !!item.id;
keyBindings.push({
action: action,
key: key,
@@ -178,9 +151,8 @@ function createKeyBindings(item) {
predefined: predefined
});
}
// Validates settings before saving
function validate() {
/* ... same as your original ... */
var valid = true;
var status = document.getElementById("status");
document
@@ -190,7 +162,7 @@ function validate() {
match = match.replace(regStrip, "");
if (match.startsWith("/")) {
try {
var regexp = new RegExp(match);
new RegExp(match);
} catch (err) {
status.textContent =
"Error: Invalid blacklist regex: " + match + ". Unable to save";
@@ -202,24 +174,45 @@ function validate() {
return valid;
}
// Saves options to chrome.storage
// MODIFIED: save_options to include nudge settings
function save_options() {
if (validate() === false) {
return;
}
keyBindings = [];
if (validate() === false) return;
keyBindings = []; // Reset global keyBindings before populating from DOM
Array.from(document.querySelectorAll(".customs")).forEach((item) =>
createKeyBindings(item)
); // Remove added shortcuts
);
var rememberSpeed = document.getElementById("rememberSpeed").checked;
var forceLastSavedSpeed = document.getElementById("forceLastSavedSpeed").checked;
var audioBoolean = document.getElementById("audioBoolean").checked;
var enabled = document.getElementById("enabled").checked;
var startHidden = document.getElementById("startHidden").checked;
var controllerOpacity = document.getElementById("controllerOpacity").value;
var blacklist = document.getElementById("blacklist").value;
var s = {}; // Object to hold all settings to be saved
s.rememberSpeed = document.getElementById("rememberSpeed").checked;
s.forceLastSavedSpeed = document.getElementById(
"forceLastSavedSpeed"
).checked;
s.audioBoolean = document.getElementById("audioBoolean").checked;
s.enabled = document.getElementById("enabled").checked;
s.startHidden = document.getElementById("startHidden").checked;
s.controllerOpacity = document.getElementById("controllerOpacity").value;
s.blacklist = document
.getElementById("blacklist")
.value.replace(regStrip, "");
s.keyBindings = keyBindings; // Use the populated global keyBindings
// ADDED: Save nudge settings
s.enableSubtitleNudge = document.getElementById(
"enableSubtitleNudge"
).checked;
s.subtitleNudgeInterval =
parseInt(document.getElementById("subtitleNudgeInterval").value, 10) ||
tcDefaults.subtitleNudgeInterval;
s.subtitleNudgeAmount =
parseFloat(document.getElementById("subtitleNudgeAmount").value) ||
tcDefaults.subtitleNudgeAmount;
// Basic validation for nudge interval and amount
if (s.subtitleNudgeInterval < 10) s.subtitleNudgeInterval = 10; // Min 10ms
if (s.subtitleNudgeAmount <= 0 || s.subtitleNudgeAmount > 0.1)
s.subtitleNudgeAmount = tcDefaults.subtitleNudgeAmount;
// Remove old flat settings (original logic)
chrome.storage.sync.remove([
"resetSpeed",
"speedStep",
@@ -233,33 +226,22 @@ function save_options() {
"advanceKeyCode",
"fastKeyCode"
]);
chrome.storage.sync.set(
{
rememberSpeed: rememberSpeed,
forceLastSavedSpeed: forceLastSavedSpeed,
audioBoolean: audioBoolean,
enabled: enabled,
startHidden: startHidden,
controllerOpacity: controllerOpacity,
keyBindings: keyBindings,
blacklist: blacklist.replace(regStrip, "")
},
function () {
// Update status to let user know options were saved.
var status = document.getElementById("status");
status.textContent = "Options saved";
setTimeout(function () {
status.textContent = "";
}, 1000);
}
);
chrome.storage.sync.set(s, function () {
var status = document.getElementById("status");
status.textContent = "Options saved";
setTimeout(function () {
status.textContent = "";
}, 1000);
});
}
// Restores options from chrome.storage
// MODIFIED: restore_options to include nudge settings
function restore_options() {
chrome.storage.sync.get(tcDefaults, function (storage) {
document.getElementById("rememberSpeed").checked = storage.rememberSpeed;
document.getElementById("forceLastSavedSpeed").checked = storage.forceLastSavedSpeed;
document.getElementById("forceLastSavedSpeed").checked =
storage.forceLastSavedSpeed;
document.getElementById("audioBoolean").checked = storage.audioBoolean;
document.getElementById("enabled").checked = storage.enabled;
document.getElementById("startHidden").checked = storage.startHidden;
@@ -267,65 +249,86 @@ function restore_options() {
storage.controllerOpacity;
document.getElementById("blacklist").value = storage.blacklist;
// ensure that there is a "display" binding for upgrades from versions that had it as a separate binding
// ADDED: Restore nudge settings
document.getElementById("enableSubtitleNudge").checked =
storage.enableSubtitleNudge;
document.getElementById("subtitleNudgeInterval").value =
storage.subtitleNudgeInterval;
document.getElementById("subtitleNudgeAmount").value =
storage.subtitleNudgeAmount;
// Original key binding restoration logic
if (
!Array.isArray(storage.keyBindings) ||
storage.keyBindings.length === 0
) {
// If keyBindings missing or not an array, use defaults from tcDefaults
storage.keyBindings = tcDefaults.keyBindings;
}
if (storage.keyBindings.filter((x) => x.action == "display").length == 0) {
storage.keyBindings.push({
action: "display",
value: 0,
force: false,
predefined: true
predefined: true,
key: storage.displayKeyCode || tcDefaults.displayKeyCode
});
}
// Clear existing dynamic shortcuts before restoring (if any were added by mistake)
const dynamicShortcuts = document.querySelectorAll(".customs:not([id])");
dynamicShortcuts.forEach((sc) => sc.remove());
for (let i in storage.keyBindings) {
var item = storage.keyBindings[i];
if (item.predefined) {
//do predefined ones because their value needed for overlay
// document.querySelector("#" + item["action"] + " .customDo").value = item["action"];
if (item["action"] == "display" && typeof item["key"] === "undefined") {
item["key"] = storage.displayKeyCode || tcDefaults.displayKeyCode; // V
item["key"] = storage.displayKeyCode || tcDefaults.displayKeyCode;
}
if (customActionsNoValues.includes(item["action"]))
document.querySelector(
if (customActionsNoValues.includes(item["action"])) {
const el = document.querySelector(
"#" + item["action"] + " .customValue"
).disabled = true;
updateCustomShortcutInputText(
document.querySelector("#" + item["action"] + " .customKey"),
item["key"]
);
if (el) el.disabled = true;
}
const keyEl = document.querySelector(
"#" + item["action"] + " .customKey"
);
document.querySelector("#" + item["action"] + " .customValue").value =
item["value"];
document.querySelector("#" + item["action"] + " .customForce").value =
item["force"];
const valEl = document.querySelector(
"#" + item["action"] + " .customValue"
);
const forceEl = document.querySelector(
"#" + item["action"] + " .customForce"
);
if (keyEl) updateCustomShortcutInputText(keyEl, item["key"]);
if (valEl) valEl.value = item["value"];
if (forceEl) forceEl.value = String(item["force"]); // Ensure string for select value
} else {
// new ones
// Non-predefined, dynamically added shortcuts
add_shortcut();
const dom = document.querySelector(".customs:last-of-type");
const dom = document.querySelector(".customs:last-of-type"); // Gets the newly added one
dom.querySelector(".customDo").value = item["action"];
if (customActionsNoValues.includes(item["action"]))
if (customActionsNoValues.includes(item["action"])) {
dom.querySelector(".customValue").disabled = true;
}
updateCustomShortcutInputText(
dom.querySelector(".customKey"),
item["key"]
);
dom.querySelector(".customValue").value = item["value"];
dom.querySelector(".customForce").value = item["force"];
dom.querySelector(".customForce").value = String(item["force"]);
}
}
});
}
function restore_defaults() {
/* ... same as your original, tcDefaults now includes nudge defaults ... */
// Remove all dynamically added shortcuts first
document.querySelectorAll(".customs:not([id])").forEach((el) => el.remove());
// Then set defaults and restore options, which will re-add predefined ones correctly
chrome.storage.sync.set(tcDefaults, function () {
restore_options();
document
.querySelectorAll(".removeParent")
.forEach((button) => button.click()); // Remove added shortcuts
// Update status to let user know options were saved.
restore_options(); // This will populate based on tcDefaults
var status = document.getElementById("status");
status.textContent = "Default options restored";
setTimeout(function () {
@@ -335,14 +338,15 @@ function restore_defaults() {
}
function show_experimental() {
/* ... same as your original ... */
document
.querySelectorAll(".customForce")
.forEach((item) => (item.style.display = "inline-block"));
}
document.addEventListener("DOMContentLoaded", function () {
/* ... same as your original event listeners setup ... */
restore_options();
document.getElementById("save").addEventListener("click", save_options);
document.getElementById("add").addEventListener("click", add_shortcut);
document
@@ -353,34 +357,32 @@ document.addEventListener("DOMContentLoaded", function () {
.addEventListener("click", show_experimental);
function eventCaller(event, className, funcName) {
if (!event.target.classList || !event.target.classList.contains(className)) {
if (!event.target.classList || !event.target.classList.contains(className))
return;
}
funcName(event);
}
document.addEventListener("keypress", (event) => {
eventCaller(event, "customValue", inputFilterNumbersOnly);
});
document.addEventListener("focus", (event) => {
eventCaller(event, "customKey", inputFocus);
});
document.addEventListener("blur", (event) => {
eventCaller(event, "customKey", inputBlur);
});
document.addEventListener("keydown", (event) => {
eventCaller(event, "customKey", recordKeyPress);
});
document.addEventListener("click", (event) => {
document.addEventListener("keypress", (event) =>
eventCaller(event, "customValue", inputFilterNumbersOnly)
);
document.addEventListener("focus", (event) =>
eventCaller(event, "customKey", inputFocus)
);
document.addEventListener("blur", (event) =>
eventCaller(event, "customKey", inputBlur)
);
document.addEventListener("keydown", (event) =>
eventCaller(event, "customKey", recordKeyPress)
);
document.addEventListener("click", (event) =>
eventCaller(event, "removeParent", function () {
event.target.parentNode.remove();
});
});
})
);
document.addEventListener("change", (event) => {
eventCaller(event, "customDo", function () {
if (customActionsNoValues.includes(event.target.value)) {
event.target.nextElementSibling.nextElementSibling.disabled = true;
event.target.nextElementSibling.nextElementSibling.value = 0;
event.target.nextElementSibling.nextElementSibling.value = 0; // Or "" if placeholder is preferred
} else {
event.target.nextElementSibling.nextElementSibling.disabled = false;
}