mirror of
https://github.com/SoPat712/videospeed.git
synced 2026-04-21 04:42:35 -04:00
1403 lines
40 KiB
JavaScript
1403 lines
40 KiB
JavaScript
var regStrip = /^[\r\t\f\v ]+|[\r\t\f\v ]+$/gm;
|
|
|
|
var keyBindings = [];
|
|
|
|
var keyCodeAliases = {
|
|
0: "null",
|
|
null: "null",
|
|
undefined: "null",
|
|
32: "Space",
|
|
37: "Left",
|
|
38: "Up",
|
|
39: "Right",
|
|
40: "Down",
|
|
96: "Num 0",
|
|
97: "Num 1",
|
|
98: "Num 2",
|
|
99: "Num 3",
|
|
100: "Num 4",
|
|
101: "Num 5",
|
|
102: "Num 6",
|
|
103: "Num 7",
|
|
104: "Num 8",
|
|
105: "Num 9",
|
|
106: "Num *",
|
|
107: "Num +",
|
|
109: "Num -",
|
|
110: "Num .",
|
|
111: "Num /",
|
|
112: "F1",
|
|
113: "F2",
|
|
114: "F3",
|
|
115: "F4",
|
|
116: "F5",
|
|
117: "F6",
|
|
118: "F7",
|
|
119: "F8",
|
|
120: "F9",
|
|
121: "F10",
|
|
122: "F11",
|
|
123: "F12",
|
|
186: ";",
|
|
188: "<",
|
|
189: "-",
|
|
187: "+",
|
|
190: ">",
|
|
191: "/",
|
|
192: "~",
|
|
219: "[",
|
|
220: "\\",
|
|
221: "]",
|
|
222: "'",
|
|
59: ";",
|
|
61: "+",
|
|
173: "-"
|
|
};
|
|
|
|
var keyCodeToKey = {
|
|
32: " ",
|
|
37: "ArrowLeft",
|
|
38: "ArrowUp",
|
|
39: "ArrowRight",
|
|
40: "ArrowDown",
|
|
96: "0",
|
|
97: "1",
|
|
98: "2",
|
|
99: "3",
|
|
100: "4",
|
|
101: "5",
|
|
102: "6",
|
|
103: "7",
|
|
104: "8",
|
|
105: "9",
|
|
106: "*",
|
|
107: "+",
|
|
109: "-",
|
|
110: ".",
|
|
111: "/",
|
|
112: "F1",
|
|
113: "F2",
|
|
114: "F3",
|
|
115: "F4",
|
|
116: "F5",
|
|
117: "F6",
|
|
118: "F7",
|
|
119: "F8",
|
|
120: "F9",
|
|
121: "F10",
|
|
122: "F11",
|
|
123: "F12",
|
|
186: ";",
|
|
188: "<",
|
|
189: "-",
|
|
187: "+",
|
|
190: ">",
|
|
191: "/",
|
|
192: "~",
|
|
219: "[",
|
|
220: "\\",
|
|
221: "]",
|
|
222: "'",
|
|
59: ";",
|
|
61: "+",
|
|
173: "-"
|
|
};
|
|
|
|
var modifierKeys = new Set([
|
|
"Alt",
|
|
"AltGraph",
|
|
"Control",
|
|
"Fn",
|
|
"Hyper",
|
|
"Meta",
|
|
"OS",
|
|
"Shift"
|
|
]);
|
|
|
|
var displayKeyAliases = {
|
|
" ": "Space",
|
|
ArrowLeft: "Left",
|
|
ArrowUp: "Up",
|
|
ArrowRight: "Right",
|
|
ArrowDown: "Down"
|
|
};
|
|
var controllerLocations = [
|
|
"top-left",
|
|
"top-center",
|
|
"top-right",
|
|
"middle-right",
|
|
"bottom-right",
|
|
"bottom-center",
|
|
"bottom-left",
|
|
"middle-left"
|
|
];
|
|
|
|
var controllerButtonDefs = {
|
|
rewind: { icon: "\u00AB", name: "Rewind" },
|
|
slower: { icon: "\u2212", name: "Decrease speed" },
|
|
faster: { icon: "+", name: "Increase speed" },
|
|
advance: { icon: "\u00BB", name: "Advance" },
|
|
display: { icon: "\u00D7", name: "Close controller" },
|
|
reset: { icon: "\u21BA", name: "Reset speed" },
|
|
fast: { icon: "\u2605", name: "Preferred speed" },
|
|
nudge: { icon: "\u2713", name: "Subtitle nudge" },
|
|
settings: { icon: "\u2699", name: "Settings" },
|
|
pause: { icon: "\u23EF", name: "Pause / Play" },
|
|
muted: { icon: "M", name: "Mute / Unmute" },
|
|
mark: { icon: "\u2691", name: "Set marker" },
|
|
jump: { icon: "\u21E5", name: "Jump to marker" }
|
|
};
|
|
|
|
function createDefaultBinding(action, key, keyCode, value) {
|
|
return {
|
|
action: action,
|
|
key: key,
|
|
keyCode: keyCode,
|
|
value: value,
|
|
force: false,
|
|
predefined: true
|
|
};
|
|
}
|
|
|
|
var tcDefaults = {
|
|
speed: 1.0,
|
|
lastSpeed: 1.0,
|
|
displayKeyCode: 86,
|
|
rememberSpeed: false,
|
|
audioBoolean: false,
|
|
startHidden: false,
|
|
hideWithYouTubeControls: false,
|
|
hideWithControls: false,
|
|
hideWithControlsTimer: 2.0,
|
|
controllerLocation: "top-left",
|
|
forceLastSavedSpeed: false,
|
|
enabled: true,
|
|
controllerOpacity: 0.3,
|
|
keyBindings: [
|
|
createDefaultBinding("display", "V", 86, 0),
|
|
createDefaultBinding("move", "P", 80, 0),
|
|
createDefaultBinding("slower", "S", 83, 0.1),
|
|
createDefaultBinding("faster", "D", 68, 0.1),
|
|
createDefaultBinding("rewind", "Z", 90, 10),
|
|
createDefaultBinding("advance", "X", 88, 10),
|
|
createDefaultBinding("reset", "R", 82, 1),
|
|
createDefaultBinding("fast", "G", 71, 1.8),
|
|
createDefaultBinding("toggleSubtitleNudge", "N", 78, 0)
|
|
],
|
|
siteRules: [
|
|
{ pattern: "youtube.com", enabled: true, enableSubtitleNudge: true },
|
|
{ pattern: "example1.com", enabled: false },
|
|
{ pattern: "/example2\\.com/i", enabled: false },
|
|
{ pattern: "/(example3|sample3)\\.com/gi", enabled: false }
|
|
],
|
|
controllerButtons: ["rewind", "slower", "faster", "advance", "display"],
|
|
showPopupControlBar: true,
|
|
popupMatchHoverControls: true,
|
|
popupControllerButtons: ["rewind", "slower", "faster", "advance", "display"],
|
|
enableSubtitleNudge: false,
|
|
subtitleNudgeInterval: 50,
|
|
subtitleNudgeAmount: 0.001
|
|
};
|
|
|
|
const actionLabels = {
|
|
display: "Show/hide controller",
|
|
move: "Move controller",
|
|
slower: "Decrease speed",
|
|
faster: "Increase speed",
|
|
rewind: "Rewind",
|
|
advance: "Advance",
|
|
reset: "Reset speed",
|
|
fast: "Preferred speed",
|
|
muted: "Mute",
|
|
pause: "Pause",
|
|
mark: "Set marker",
|
|
jump: "Jump to marker",
|
|
toggleSubtitleNudge: "Toggle subtitle nudge"
|
|
};
|
|
|
|
const customActionsNoValues = [
|
|
"reset",
|
|
"display",
|
|
"move",
|
|
"muted",
|
|
"pause",
|
|
"mark",
|
|
"jump",
|
|
"toggleSubtitleNudge"
|
|
];
|
|
|
|
function refreshAddShortcutSelector() {
|
|
const selector = document.getElementById("addShortcutSelector");
|
|
if (!selector) return;
|
|
|
|
// Clear existing options except the first one
|
|
while (selector.options.length > 1) {
|
|
selector.remove(1);
|
|
}
|
|
|
|
// Find all currently used actions
|
|
const usedActions = new Set();
|
|
document.querySelectorAll(".shortcut-row").forEach((row) => {
|
|
const action = row.dataset.action;
|
|
if (action) {
|
|
usedActions.add(action);
|
|
}
|
|
});
|
|
|
|
// Add all unused actions
|
|
Object.keys(actionLabels).forEach((action) => {
|
|
if (!usedActions.has(action)) {
|
|
const option = document.createElement("option");
|
|
option.value = action;
|
|
option.text = actionLabels[action];
|
|
selector.appendChild(option);
|
|
}
|
|
});
|
|
|
|
// If no available actions, hide or disable the selector
|
|
if (selector.options.length === 1) {
|
|
selector.disabled = true;
|
|
selector.options[0].text = "All shortcuts added";
|
|
} else {
|
|
selector.disabled = false;
|
|
selector.options[0].text = "Add shortcut\u2026";
|
|
}
|
|
}
|
|
|
|
function ensureDefaultBinding(storage, action, key, keyCode, value) {
|
|
if (storage.keyBindings.some((item) => item.action === action)) return;
|
|
|
|
storage.keyBindings.push(createDefaultBinding(action, key, keyCode, value));
|
|
}
|
|
|
|
function normalizeControllerLocation(location) {
|
|
if (controllerLocations.includes(location)) return location;
|
|
return tcDefaults.controllerLocation;
|
|
}
|
|
|
|
function normalizeBindingKey(key) {
|
|
if (typeof key !== "string" || key.length === 0) return null;
|
|
if (key === "Spacebar") return " ";
|
|
if (key === "Esc") return "Escape";
|
|
if (key.length === 1 && /[a-z]/i.test(key)) return key.toUpperCase();
|
|
return key;
|
|
}
|
|
|
|
function getLegacyKeyCode(binding) {
|
|
if (!binding) return null;
|
|
if (Number.isInteger(binding.keyCode)) return binding.keyCode;
|
|
if (typeof binding.key === "number" && Number.isInteger(binding.key)) {
|
|
return binding.key;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function legacyKeyCodeToBinding(keyCode) {
|
|
if (!Number.isInteger(keyCode)) return null;
|
|
var normalizedKey = keyCodeToKey[keyCode];
|
|
if (!normalizedKey && keyCode >= 48 && keyCode <= 57) {
|
|
normalizedKey = String.fromCharCode(keyCode);
|
|
}
|
|
if (!normalizedKey && keyCode >= 65 && keyCode <= 90) {
|
|
normalizedKey = String.fromCharCode(keyCode);
|
|
}
|
|
return {
|
|
key: normalizeBindingKey(normalizedKey),
|
|
keyCode: keyCode,
|
|
code: null,
|
|
disabled: false
|
|
};
|
|
}
|
|
|
|
function createDisabledBinding() {
|
|
return {
|
|
key: null,
|
|
keyCode: null,
|
|
code: null,
|
|
disabled: true
|
|
};
|
|
}
|
|
|
|
function normalizeStoredBinding(binding, fallbackKeyCode) {
|
|
var fallbackBinding = legacyKeyCodeToBinding(fallbackKeyCode);
|
|
if (!binding) {
|
|
return fallbackBinding;
|
|
}
|
|
|
|
if (
|
|
binding.disabled === true ||
|
|
(binding.key === null &&
|
|
binding.keyCode === null &&
|
|
binding.code === null)
|
|
) {
|
|
return createDisabledBinding();
|
|
}
|
|
|
|
var normalized = {
|
|
key: null,
|
|
keyCode: null,
|
|
code:
|
|
typeof binding.code === "string" && binding.code.length > 0
|
|
? binding.code
|
|
: null,
|
|
disabled: false
|
|
};
|
|
|
|
if (typeof binding.key === "string") {
|
|
normalized.key = normalizeBindingKey(binding.key);
|
|
}
|
|
|
|
var legacyKeyCode = getLegacyKeyCode(binding);
|
|
if (Number.isInteger(legacyKeyCode)) {
|
|
var legacyBinding = legacyKeyCodeToBinding(legacyKeyCode);
|
|
if (legacyBinding) {
|
|
normalized.key = normalized.key || legacyBinding.key;
|
|
normalized.keyCode = legacyKeyCode;
|
|
}
|
|
}
|
|
|
|
if (Number.isInteger(binding.keyCode)) {
|
|
normalized.keyCode = binding.keyCode;
|
|
}
|
|
|
|
if (!normalized.key && fallbackBinding) {
|
|
normalized.key = fallbackBinding.key;
|
|
if (normalized.keyCode === null) normalized.keyCode = fallbackBinding.keyCode;
|
|
}
|
|
|
|
if (!normalized.key && !normalized.code && normalized.keyCode === null) {
|
|
return null;
|
|
}
|
|
|
|
return normalized;
|
|
}
|
|
|
|
function getBindingLabel(binding) {
|
|
if (!binding) return "";
|
|
if (binding.disabled) return "";
|
|
if (binding.key) {
|
|
return displayKeyAliases[binding.key] || binding.key;
|
|
}
|
|
var legacyKeyCode = getLegacyKeyCode(binding);
|
|
if (keyCodeAliases[legacyKeyCode]) return keyCodeAliases[legacyKeyCode];
|
|
if (Number.isInteger(legacyKeyCode)) return String.fromCharCode(legacyKeyCode);
|
|
return "";
|
|
}
|
|
|
|
function setShortcutInputBinding(input, binding) {
|
|
input.vscBinding = binding ? Object.assign({}, binding) : null;
|
|
input.keyCode =
|
|
binding && Number.isInteger(binding.keyCode) ? binding.keyCode : null;
|
|
input.value = getBindingLabel(binding);
|
|
}
|
|
|
|
function captureBindingFromEvent(event) {
|
|
var normalizedKey = normalizeBindingKey(event.key);
|
|
if (!normalizedKey || modifierKeys.has(normalizedKey)) return null;
|
|
return {
|
|
key: normalizedKey,
|
|
keyCode: Number.isInteger(event.keyCode) ? event.keyCode : null,
|
|
code: event.code || null,
|
|
disabled: false
|
|
};
|
|
}
|
|
|
|
function recordKeyPress(event) {
|
|
if (event.key === "Tab") return;
|
|
|
|
if (event.key === "Backspace") {
|
|
setShortcutInputBinding(event.target, null);
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return;
|
|
}
|
|
|
|
if (event.key === "Escape") {
|
|
setShortcutInputBinding(event.target, createDisabledBinding());
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return;
|
|
}
|
|
|
|
var binding = captureBindingFromEvent(event);
|
|
if (!binding) return;
|
|
|
|
setShortcutInputBinding(event.target, binding);
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
}
|
|
|
|
function inputFilterNumbersOnly(event) {
|
|
var char = String.fromCharCode(event.keyCode);
|
|
if (
|
|
!/[\d\.]$/.test(char) ||
|
|
!/^\d+(\.\d*)?$/.test(event.target.value + char)
|
|
) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
}
|
|
}
|
|
|
|
function inputFocus(event) {
|
|
event.target.value = "";
|
|
}
|
|
|
|
function inputBlur(event) {
|
|
setShortcutInputBinding(event.target, event.target.vscBinding || null);
|
|
}
|
|
|
|
function updateCustomShortcutInputText(inputItem, bindingOrKeyCode) {
|
|
if (
|
|
bindingOrKeyCode &&
|
|
typeof bindingOrKeyCode === "object" &&
|
|
!Array.isArray(bindingOrKeyCode)
|
|
) {
|
|
setShortcutInputBinding(inputItem, bindingOrKeyCode);
|
|
return;
|
|
}
|
|
|
|
setShortcutInputBinding(inputItem, legacyKeyCodeToBinding(bindingOrKeyCode));
|
|
}
|
|
|
|
function appendSelectOptions(select, options) {
|
|
options.forEach(function (optionData) {
|
|
var option = document.createElement("option");
|
|
option.value = optionData.value;
|
|
option.textContent = optionData.label;
|
|
select.appendChild(option);
|
|
});
|
|
}
|
|
|
|
function add_shortcut(action, value) {
|
|
if (!action) return;
|
|
|
|
var div = document.createElement("div");
|
|
div.setAttribute("class", "shortcut-row customs");
|
|
div.dataset.action = action;
|
|
|
|
var actionLabel = document.createElement("div");
|
|
actionLabel.className = "shortcut-label";
|
|
actionLabel.textContent = actionLabels[action] || action;
|
|
|
|
var keyInput = document.createElement("input");
|
|
keyInput.className = "customKey";
|
|
keyInput.type = "text";
|
|
keyInput.placeholder = "press a key";
|
|
|
|
var valueInput = document.createElement("input");
|
|
valueInput.className = "customValue";
|
|
valueInput.type = "text";
|
|
valueInput.placeholder = "value";
|
|
if (customActionsNoValues.includes(action)) {
|
|
valueInput.value = "N/A";
|
|
valueInput.disabled = true;
|
|
} else {
|
|
valueInput.value = value || 0;
|
|
}
|
|
|
|
var removeButton = document.createElement("button");
|
|
removeButton.className = "removeParent";
|
|
removeButton.type = "button";
|
|
removeButton.textContent = "\u00d7";
|
|
|
|
div.appendChild(actionLabel);
|
|
div.appendChild(keyInput);
|
|
div.appendChild(valueInput);
|
|
div.appendChild(removeButton);
|
|
|
|
var customsElement = document.querySelector(".shortcuts-grid");
|
|
customsElement.appendChild(div);
|
|
|
|
refreshAddShortcutSelector();
|
|
}
|
|
|
|
function createKeyBindings(item) {
|
|
var action = item.dataset.action || item.querySelector(".customDo").value;
|
|
var input = item.querySelector(".customKey");
|
|
var valueInput = item.querySelector(".customValue");
|
|
var predefined = !!item.id;
|
|
var fallbackKeyCode =
|
|
predefined && action === "display"
|
|
? tcDefaults.displayKeyCode
|
|
: undefined;
|
|
var binding = normalizeStoredBinding(input.vscBinding, fallbackKeyCode);
|
|
|
|
if (!binding) {
|
|
return {
|
|
valid: false,
|
|
message: "Error: Shortcut for " + action + " is invalid. Unable to save"
|
|
};
|
|
}
|
|
|
|
keyBindings.push({
|
|
action: action,
|
|
key: binding.key,
|
|
keyCode: binding.keyCode,
|
|
code: binding.code,
|
|
disabled: binding.disabled === true,
|
|
value: customActionsNoValues.includes(action)
|
|
? 0
|
|
: Number(valueInput.value),
|
|
force: false,
|
|
predefined: predefined
|
|
});
|
|
|
|
return { valid: true };
|
|
}
|
|
|
|
function validate() {
|
|
var valid = true;
|
|
var status = document.getElementById("status");
|
|
|
|
// Validate site rules patterns
|
|
document.querySelectorAll(".site-rule").forEach((ruleEl) => {
|
|
var pattern = ruleEl.querySelector(".site-pattern").value.trim();
|
|
if (pattern.length === 0) return;
|
|
|
|
if (pattern.startsWith("/")) {
|
|
try {
|
|
var lastSlash = pattern.lastIndexOf("/");
|
|
if (lastSlash > 0) {
|
|
new RegExp(pattern.substring(1, lastSlash), pattern.substring(lastSlash + 1));
|
|
}
|
|
} catch (err) {
|
|
status.textContent =
|
|
"Error: Invalid site rule regex: " + pattern + ". Unable to save";
|
|
valid = false;
|
|
return;
|
|
}
|
|
}
|
|
});
|
|
|
|
return valid;
|
|
}
|
|
|
|
function save_options() {
|
|
if (validate() === false) return;
|
|
|
|
keyBindings = [];
|
|
var status = document.getElementById("status");
|
|
var saveError = null;
|
|
|
|
// Collect shortcuts from the main shortcuts section (both default and custom)
|
|
Array.from(document.querySelectorAll("#customs .shortcut-row")).forEach((item) => {
|
|
if (saveError) return;
|
|
var result = createKeyBindings(item);
|
|
if (!result.valid) saveError = result.message;
|
|
});
|
|
|
|
if (saveError) {
|
|
status.textContent = saveError;
|
|
return;
|
|
}
|
|
|
|
var settings = {};
|
|
settings.rememberSpeed = document.getElementById("rememberSpeed").checked;
|
|
settings.forceLastSavedSpeed =
|
|
document.getElementById("forceLastSavedSpeed").checked;
|
|
settings.audioBoolean = document.getElementById("audioBoolean").checked;
|
|
settings.enabled = document.getElementById("enabled").checked;
|
|
settings.startHidden = document.getElementById("startHidden").checked;
|
|
settings.hideWithControls = document.getElementById("hideWithControls").checked;
|
|
settings.hideWithControlsTimer =
|
|
Math.min(15, Math.max(0.1, parseFloat(document.getElementById("hideWithControlsTimer").value) || tcDefaults.hideWithControlsTimer));
|
|
|
|
// Sync back to the legacy key if it exists, for backward compatibility
|
|
settings.hideWithYouTubeControls = settings.hideWithControls;
|
|
|
|
if (settings.hideWithControlsTimer < 0.1) settings.hideWithControlsTimer = 0.1;
|
|
if (settings.hideWithControlsTimer > 15) settings.hideWithControlsTimer = 15;
|
|
|
|
settings.controllerLocation = normalizeControllerLocation(
|
|
document.getElementById("controllerLocation").value
|
|
);
|
|
settings.controllerOpacity =
|
|
parseFloat(document.getElementById("controllerOpacity").value) ||
|
|
tcDefaults.controllerOpacity;
|
|
settings.keyBindings = keyBindings;
|
|
settings.enableSubtitleNudge =
|
|
document.getElementById("enableSubtitleNudge").checked;
|
|
settings.subtitleNudgeInterval =
|
|
parseInt(document.getElementById("subtitleNudgeInterval").value, 10) ||
|
|
tcDefaults.subtitleNudgeInterval;
|
|
settings.subtitleNudgeAmount = tcDefaults.subtitleNudgeAmount;
|
|
|
|
if (settings.subtitleNudgeInterval < 10) {
|
|
settings.subtitleNudgeInterval = 10;
|
|
}
|
|
if (settings.subtitleNudgeInterval > 1000) {
|
|
settings.subtitleNudgeInterval = 1000;
|
|
}
|
|
|
|
settings.controllerButtons = getControlBarOrder();
|
|
settings.showPopupControlBar =
|
|
document.getElementById("showPopupControlBar").checked;
|
|
settings.popupMatchHoverControls =
|
|
document.getElementById("popupMatchHoverControls").checked;
|
|
settings.popupControllerButtons = getPopupControlBarOrder();
|
|
|
|
// Collect site rules
|
|
settings.siteRules = [];
|
|
document.querySelectorAll(".site-rule").forEach((ruleEl) => {
|
|
var pattern = ruleEl.querySelector(".site-pattern").value.trim();
|
|
if (pattern.length === 0) return;
|
|
|
|
var rule = { pattern: pattern };
|
|
|
|
// Handle Enable toggle
|
|
rule.enabled = ruleEl.querySelector(".site-enabled").checked;
|
|
|
|
// Handle other site settings
|
|
const siteSettings = [
|
|
{ key: "startHidden", type: "checkbox" },
|
|
{ key: "hideWithControls", type: "checkbox" },
|
|
{ key: "hideWithControlsTimer", type: "text" },
|
|
{ key: "controllerLocation", type: "select" },
|
|
{ key: "rememberSpeed", type: "checkbox" },
|
|
{ key: "forceLastSavedSpeed", type: "checkbox" },
|
|
{ key: "audioBoolean", type: "checkbox" },
|
|
{ key: "controllerOpacity", type: "text" },
|
|
{ key: "showPopupControlBar", type: "checkbox" },
|
|
{ key: "enableSubtitleNudge", type: "checkbox" },
|
|
{ key: "subtitleNudgeInterval", type: "text" }
|
|
];
|
|
|
|
siteSettings.forEach((s) => {
|
|
var input = ruleEl.querySelector(`.site-${s.key}`);
|
|
if (!input) return;
|
|
var siteValue;
|
|
if (s.type === "checkbox") {
|
|
siteValue = input.checked;
|
|
} else {
|
|
siteValue = input.value;
|
|
}
|
|
var globalInput = document.getElementById(s.key);
|
|
if (globalInput) {
|
|
var globalValue = s.type === "checkbox" ? globalInput.checked : globalInput.value;
|
|
if (String(siteValue) !== String(globalValue)) {
|
|
rule[s.key] = siteValue;
|
|
}
|
|
} else {
|
|
rule[s.key] = siteValue;
|
|
}
|
|
});
|
|
|
|
if (ruleEl.querySelector(".override-controlbar").checked) {
|
|
var activeZone = ruleEl.querySelector(".site-cb-active");
|
|
if (activeZone) {
|
|
rule.controllerButtons = readControlBarOrder(activeZone);
|
|
}
|
|
}
|
|
|
|
if (ruleEl.querySelector(".override-popup-controlbar").checked) {
|
|
var popupActiveZone = ruleEl.querySelector(".site-popup-cb-active");
|
|
if (popupActiveZone) {
|
|
rule.popupControllerButtons = readControlBarOrder(popupActiveZone);
|
|
}
|
|
}
|
|
|
|
if (ruleEl.querySelector(".override-shortcuts").checked) {
|
|
var shortcuts = [];
|
|
ruleEl.querySelectorAll(".site-shortcuts-container .customs").forEach((shortcutRow) => {
|
|
var action = shortcutRow.dataset.action;
|
|
var keyInput = shortcutRow.querySelector(".customKey");
|
|
var valueInput = shortcutRow.querySelector(".customValue");
|
|
var forceCheckbox = shortcutRow.querySelector(".customForce");
|
|
var binding = normalizeStoredBinding(keyInput.vscBinding);
|
|
|
|
if (binding) {
|
|
shortcuts.push({
|
|
action: action,
|
|
key: binding.key,
|
|
keyCode: binding.keyCode,
|
|
code: binding.code,
|
|
disabled: binding.disabled === true,
|
|
value: customActionsNoValues.includes(action)
|
|
? 0
|
|
: Number(valueInput.value),
|
|
force: forceCheckbox ? forceCheckbox.checked : false
|
|
});
|
|
}
|
|
});
|
|
if (shortcuts.length > 0) rule.shortcuts = shortcuts;
|
|
}
|
|
|
|
settings.siteRules.push(rule);
|
|
});
|
|
|
|
// Legacy keys to remove
|
|
const legacyKeys = [
|
|
"resetSpeed", "speedStep", "fastSpeed", "rewindTime", "advanceTime",
|
|
"resetKeyCode", "slowerKeyCode", "fasterKeyCode", "rewindKeyCode",
|
|
"advanceKeyCode", "fastKeyCode", "blacklist"
|
|
];
|
|
|
|
chrome.storage.sync.remove(legacyKeys, function () {
|
|
chrome.storage.sync.set(settings, function () {
|
|
status.textContent = "Options saved";
|
|
setTimeout(function () {
|
|
status.textContent = "";
|
|
}, 1000);
|
|
});
|
|
});
|
|
}
|
|
|
|
function ensureAllDefaultBindings(storage) {
|
|
tcDefaults.keyBindings.forEach((binding) => {
|
|
// Special case for "display" to support legacy displayKeyCode
|
|
if (binding.action === "display" && storage.displayKeyCode) {
|
|
ensureDefaultBinding(storage, "display", "V", storage.displayKeyCode, 0);
|
|
} else {
|
|
ensureDefaultBinding(
|
|
storage,
|
|
binding.action,
|
|
binding.key,
|
|
binding.keyCode,
|
|
binding.value
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
function migrateLegacyBlacklist(storage) {
|
|
if (!storage.blacklist || typeof storage.blacklist !== "string") {
|
|
return [];
|
|
}
|
|
|
|
var siteRules = [];
|
|
var lines = storage.blacklist.split("\n");
|
|
|
|
lines.forEach((line) => {
|
|
var pattern = line.replace(regStrip, "");
|
|
if (pattern.length === 0) return;
|
|
|
|
siteRules.push({
|
|
pattern: pattern,
|
|
disableExtension: true
|
|
});
|
|
});
|
|
|
|
return siteRules;
|
|
}
|
|
|
|
function addSiteRuleShortcut(container, action, binding, value, force) {
|
|
var div = document.createElement("div");
|
|
div.setAttribute("class", "shortcut-row customs");
|
|
div.dataset.action = action;
|
|
|
|
var actionLabel = document.createElement("div");
|
|
actionLabel.className = "shortcut-label";
|
|
var actionLabels = {
|
|
display: "Show/hide controller",
|
|
move: "Move controller",
|
|
slower: "Decrease speed",
|
|
faster: "Increase speed",
|
|
rewind: "Rewind",
|
|
advance: "Advance",
|
|
reset: "Reset speed",
|
|
fast: "Preferred speed",
|
|
muted: "Mute",
|
|
pause: "Pause",
|
|
mark: "Set marker",
|
|
jump: "Jump to marker",
|
|
toggleSubtitleNudge: "Toggle subtitle nudge"
|
|
};
|
|
var actionLabelText = actionLabels[action] || action;
|
|
if (action === "toggleSubtitleNudge") {
|
|
// Check if the site rule is for YouTube.
|
|
// We look up the pattern from the site rule element this container belongs to.
|
|
var ruleEl = container.closest(".site-rule");
|
|
var pattern = ruleEl ? ruleEl.querySelector(".site-pattern").value : "";
|
|
if (!pattern.toLowerCase().includes("youtube.com")) {
|
|
actionLabelText += " (only for YouTube embeds)";
|
|
}
|
|
}
|
|
actionLabel.textContent = actionLabelText;
|
|
|
|
var keyInput = document.createElement("input");
|
|
keyInput.className = "customKey";
|
|
keyInput.type = "text";
|
|
keyInput.placeholder = "press a key";
|
|
updateCustomShortcutInputText(keyInput, binding || createDisabledBinding());
|
|
|
|
var valueInput = document.createElement("input");
|
|
valueInput.className = "customValue";
|
|
valueInput.type = "text";
|
|
valueInput.placeholder = "value (0.10)";
|
|
valueInput.value = value || 0;
|
|
if (customActionsNoValues.includes(action)) {
|
|
valueInput.disabled = true;
|
|
}
|
|
|
|
var forceLabel = document.createElement("label");
|
|
forceLabel.className = "force-label";
|
|
forceLabel.title = "Prevent website from capturing this key";
|
|
|
|
var forceCheckbox = document.createElement("input");
|
|
forceCheckbox.type = "checkbox";
|
|
forceCheckbox.className = "customForce";
|
|
forceCheckbox.checked = force === true || force === "true";
|
|
|
|
var forceText = document.createElement("span");
|
|
forceText.textContent = "Block site from capturing keypress";
|
|
forceText.className = "force-text";
|
|
|
|
forceLabel.appendChild(forceCheckbox);
|
|
forceLabel.appendChild(forceText);
|
|
|
|
div.appendChild(actionLabel);
|
|
div.appendChild(keyInput);
|
|
div.appendChild(valueInput);
|
|
div.appendChild(forceLabel);
|
|
|
|
container.appendChild(div);
|
|
}
|
|
|
|
function createSiteRule(rule) {
|
|
var template = document.getElementById("siteRuleTemplate");
|
|
var clone = template.content.cloneNode(true);
|
|
var ruleEl = clone.querySelector(".site-rule");
|
|
|
|
var pattern = rule && rule.pattern ? rule.pattern : "";
|
|
ruleEl.querySelector(".site-pattern").value = pattern;
|
|
|
|
// Make the rule body collapsed by default
|
|
var ruleBody = ruleEl.querySelector(".site-rule-body");
|
|
ruleBody.style.display = "none";
|
|
ruleEl.classList.add("collapsed");
|
|
|
|
var enabledCheckbox = ruleEl.querySelector(".site-enabled");
|
|
var contentEl = ruleEl.querySelector(".site-rule-content");
|
|
|
|
function updateDisabledState() {
|
|
if (enabledCheckbox.checked) {
|
|
contentEl.classList.remove("disabled-rule");
|
|
} else {
|
|
contentEl.classList.add("disabled-rule");
|
|
}
|
|
}
|
|
|
|
enabledCheckbox.addEventListener("change", updateDisabledState);
|
|
|
|
if (rule) {
|
|
if (rule.enabled !== undefined) {
|
|
enabledCheckbox.checked = rule.enabled;
|
|
} else if (rule.disableExtension !== undefined) {
|
|
enabledCheckbox.checked = !rule.disableExtension;
|
|
} else {
|
|
enabledCheckbox.checked = true;
|
|
}
|
|
} else {
|
|
enabledCheckbox.checked = true;
|
|
}
|
|
updateDisabledState();
|
|
|
|
const settings = [
|
|
{ key: "startHidden", type: "checkbox" },
|
|
{ key: "hideWithControls", type: "checkbox" },
|
|
{ key: "hideWithControlsTimer", type: "text" },
|
|
{ key: "controllerLocation", type: "select" },
|
|
{ key: "rememberSpeed", type: "checkbox" },
|
|
{ key: "forceLastSavedSpeed", type: "checkbox" },
|
|
{ key: "audioBoolean", type: "checkbox" },
|
|
{ key: "controllerOpacity", type: "text" },
|
|
{ key: "showPopupControlBar", type: "checkbox" },
|
|
{ key: "enableSubtitleNudge", type: "checkbox" },
|
|
{ key: "subtitleNudgeInterval", type: "text" }
|
|
];
|
|
|
|
settings.forEach((s) => {
|
|
var input = ruleEl.querySelector(`.site-${s.key}`);
|
|
if (!input) return;
|
|
|
|
var value;
|
|
if (rule && rule[s.key] !== undefined) {
|
|
value = rule[s.key];
|
|
} else {
|
|
// Initialize with current global value
|
|
var globalInput = document.getElementById(s.key);
|
|
if (globalInput) {
|
|
if (s.type === "checkbox") {
|
|
value = globalInput.checked;
|
|
} else {
|
|
value = globalInput.value;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (s.type === "checkbox") {
|
|
input.checked = value;
|
|
} else {
|
|
input.value = value;
|
|
}
|
|
});
|
|
|
|
if (rule && Array.isArray(rule.controllerButtons) && rule.controllerButtons.length > 0) {
|
|
ruleEl.querySelector(".override-controlbar").checked = true;
|
|
var cbContainer = ruleEl.querySelector(".site-controlbar-container");
|
|
cbContainer.style.display = "block";
|
|
populateControlBarZones(
|
|
ruleEl.querySelector(".site-cb-active"),
|
|
ruleEl.querySelector(".site-cb-available"),
|
|
rule.controllerButtons
|
|
);
|
|
}
|
|
|
|
if (rule && Array.isArray(rule.popupControllerButtons) && rule.popupControllerButtons.length > 0) {
|
|
ruleEl.querySelector(".override-popup-controlbar").checked = true;
|
|
var popupCbContainer = ruleEl.querySelector(".site-popup-controlbar-container");
|
|
popupCbContainer.style.display = "block";
|
|
populateControlBarZones(
|
|
ruleEl.querySelector(".site-popup-cb-active"),
|
|
ruleEl.querySelector(".site-popup-cb-available"),
|
|
rule.popupControllerButtons
|
|
);
|
|
}
|
|
|
|
if (rule && Array.isArray(rule.shortcuts) && rule.shortcuts.length > 0) {
|
|
ruleEl.querySelector(".override-shortcuts").checked = true;
|
|
var container = ruleEl.querySelector(".site-shortcuts-container");
|
|
container.style.display = "block";
|
|
|
|
rule.shortcuts.forEach((shortcut) => {
|
|
addSiteRuleShortcut(
|
|
container,
|
|
shortcut.action,
|
|
shortcut,
|
|
shortcut.value,
|
|
shortcut.force
|
|
);
|
|
});
|
|
}
|
|
|
|
document.getElementById("siteRulesContainer").appendChild(ruleEl);
|
|
}
|
|
|
|
function populateDefaultSiteShortcuts(container) {
|
|
tcDefaults.keyBindings.forEach((binding) => {
|
|
addSiteRuleShortcut(container, binding.action, binding, binding.value, false);
|
|
});
|
|
}
|
|
|
|
function createControlBarBlock(buttonId) {
|
|
var def = controllerButtonDefs[buttonId];
|
|
if (!def) return null;
|
|
|
|
var block = document.createElement("div");
|
|
block.className = "cb-block";
|
|
block.dataset.buttonId = buttonId;
|
|
block.draggable = true;
|
|
|
|
var grip = document.createElement("span");
|
|
grip.className = "cb-grip";
|
|
|
|
var icon = document.createElement("span");
|
|
icon.className = "cb-icon";
|
|
icon.textContent = def.icon;
|
|
|
|
var label = document.createElement("span");
|
|
label.className = "cb-label";
|
|
label.textContent = def.name;
|
|
|
|
block.appendChild(grip);
|
|
block.appendChild(icon);
|
|
block.appendChild(label);
|
|
|
|
return block;
|
|
}
|
|
|
|
function populateControlBarZones(activeZone, availableZone, activeIds) {
|
|
activeZone.innerHTML = "";
|
|
availableZone.innerHTML = "";
|
|
|
|
activeIds.forEach(function (id) {
|
|
var block = createControlBarBlock(id);
|
|
if (block) activeZone.appendChild(block);
|
|
});
|
|
|
|
Object.keys(controllerButtonDefs).forEach(function (id) {
|
|
if (!activeIds.includes(id)) {
|
|
var block = createControlBarBlock(id);
|
|
if (block) availableZone.appendChild(block);
|
|
}
|
|
});
|
|
}
|
|
|
|
function readControlBarOrder(activeZone) {
|
|
var blocks = activeZone.querySelectorAll(".cb-block");
|
|
return Array.from(blocks).map(function (block) {
|
|
return block.dataset.buttonId;
|
|
});
|
|
}
|
|
|
|
function populateControlBarEditor(activeIds) {
|
|
populateControlBarZones(
|
|
document.getElementById("controlBarActive"),
|
|
document.getElementById("controlBarAvailable"),
|
|
activeIds
|
|
);
|
|
}
|
|
|
|
function getControlBarOrder() {
|
|
return readControlBarOrder(document.getElementById("controlBarActive"));
|
|
}
|
|
|
|
function populatePopupControlBarEditor(activeIds) {
|
|
populateControlBarZones(
|
|
document.getElementById("popupControlBarActive"),
|
|
document.getElementById("popupControlBarAvailable"),
|
|
activeIds
|
|
);
|
|
}
|
|
|
|
function getPopupControlBarOrder() {
|
|
return readControlBarOrder(document.getElementById("popupControlBarActive"));
|
|
}
|
|
|
|
function updatePopupEditorDisabledState() {
|
|
var checkbox = document.getElementById("popupMatchHoverControls");
|
|
var wrap = document.getElementById("popupCbEditorWrap");
|
|
if (!checkbox || !wrap) return;
|
|
if (checkbox.checked) {
|
|
wrap.classList.add("cb-editor-disabled");
|
|
} else {
|
|
wrap.classList.remove("cb-editor-disabled");
|
|
}
|
|
}
|
|
|
|
function getDragAfterElement(container, x, y) {
|
|
var elements = Array.from(
|
|
container.querySelectorAll(".cb-block:not(.cb-dragging)")
|
|
);
|
|
|
|
for (var i = 0; i < elements.length; i++) {
|
|
var box = elements[i].getBoundingClientRect();
|
|
var centerX = box.left + box.width / 2;
|
|
var centerY = box.top + box.height / 2;
|
|
var rowThresh = box.height * 0.5;
|
|
|
|
if (y - centerY > rowThresh) continue;
|
|
if (centerY - y > rowThresh) return elements[i];
|
|
if (x < centerX) return elements[i];
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
function initControlBarEditor() {
|
|
var zones = document.querySelectorAll(".cb-dropzone");
|
|
var draggedBlock = null;
|
|
|
|
document.addEventListener("dragstart", function (e) {
|
|
var block = e.target.closest(".cb-block");
|
|
if (!block) return;
|
|
draggedBlock = block;
|
|
e.dataTransfer.effectAllowed = "move";
|
|
e.dataTransfer.setData("text/plain", block.dataset.buttonId);
|
|
requestAnimationFrame(function () {
|
|
block.classList.add("cb-dragging");
|
|
});
|
|
});
|
|
|
|
document.addEventListener("dragend", function (e) {
|
|
var block = e.target.closest(".cb-block");
|
|
if (!block) return;
|
|
block.classList.remove("cb-dragging");
|
|
draggedBlock = null;
|
|
zones.forEach(function (zone) {
|
|
zone.classList.remove("cb-over");
|
|
});
|
|
});
|
|
|
|
zones.forEach(function (zone) {
|
|
zone.addEventListener("dragover", function (e) {
|
|
e.preventDefault();
|
|
e.dataTransfer.dropEffect = "move";
|
|
zone.classList.add("cb-over");
|
|
|
|
if (!draggedBlock) return;
|
|
|
|
var afterEl = getDragAfterElement(zone, e.clientX, e.clientY);
|
|
if (afterEl) {
|
|
zone.insertBefore(draggedBlock, afterEl);
|
|
} else {
|
|
zone.appendChild(draggedBlock);
|
|
}
|
|
});
|
|
|
|
zone.addEventListener("dragleave", function (e) {
|
|
if (zone.contains(e.relatedTarget)) return;
|
|
zone.classList.remove("cb-over");
|
|
});
|
|
|
|
zone.addEventListener("drop", function (e) {
|
|
e.preventDefault();
|
|
zone.classList.remove("cb-over");
|
|
});
|
|
});
|
|
}
|
|
|
|
function restore_options() {
|
|
chrome.storage.sync.get(tcDefaults, function (storage) {
|
|
document.getElementById("rememberSpeed").checked = storage.rememberSpeed;
|
|
document.getElementById("forceLastSavedSpeed").checked =
|
|
storage.forceLastSavedSpeed;
|
|
document.getElementById("audioBoolean").checked = storage.audioBoolean;
|
|
document.getElementById("enabled").checked = storage.enabled;
|
|
document.getElementById("startHidden").checked = storage.startHidden;
|
|
|
|
// Migration/Normalization for hideWithControls
|
|
const hideWithControls = typeof storage.hideWithControls !== "undefined"
|
|
? storage.hideWithControls
|
|
: storage.hideWithYouTubeControls;
|
|
|
|
document.getElementById("hideWithControls").checked = hideWithControls;
|
|
document.getElementById("hideWithControlsTimer").value =
|
|
storage.hideWithControlsTimer || tcDefaults.hideWithControlsTimer;
|
|
|
|
document.getElementById("controllerLocation").value =
|
|
normalizeControllerLocation(storage.controllerLocation);
|
|
document.getElementById("controllerOpacity").value =
|
|
storage.controllerOpacity;
|
|
document.getElementById("showPopupControlBar").checked =
|
|
storage.showPopupControlBar !== false;
|
|
document.getElementById("enableSubtitleNudge").checked =
|
|
storage.enableSubtitleNudge;
|
|
document.getElementById("subtitleNudgeInterval").value =
|
|
storage.subtitleNudgeInterval;
|
|
|
|
if (!Array.isArray(storage.keyBindings) || storage.keyBindings.length === 0) {
|
|
storage.keyBindings = tcDefaults.keyBindings.slice();
|
|
}
|
|
|
|
ensureAllDefaultBindings(storage);
|
|
|
|
document.querySelectorAll(".customs:not([id])").forEach((row) => row.remove());
|
|
|
|
storage.keyBindings.forEach((item) => {
|
|
var row = document.getElementById(item.action);
|
|
var normalizedBinding = normalizeStoredBinding(item);
|
|
|
|
if (!row) {
|
|
add_shortcut(item.action, item.value);
|
|
row = document.querySelector(".shortcut-row.customs:last-of-type");
|
|
}
|
|
|
|
if (!row) return;
|
|
|
|
var keyInput = row.querySelector(".customKey");
|
|
if (keyInput) {
|
|
updateCustomShortcutInputText(keyInput, normalizedBinding || null);
|
|
}
|
|
|
|
var valueInput = row.querySelector(".customValue");
|
|
if (customActionsNoValues.includes(item.action)) {
|
|
if (valueInput) {
|
|
valueInput.value = "N/A";
|
|
valueInput.disabled = true;
|
|
}
|
|
} else if (valueInput) {
|
|
valueInput.value = item.value;
|
|
}
|
|
});
|
|
|
|
refreshAddShortcutSelector();
|
|
|
|
// Load site rules (use defaults if none in storage or if storage has empty array)
|
|
var siteRules = Array.isArray(storage.siteRules) && storage.siteRules.length > 0
|
|
? storage.siteRules
|
|
: (storage.blacklist ? migrateLegacyBlacklist(storage) : (tcDefaults.siteRules || []));
|
|
|
|
// If we migrated from blacklist, save the new format
|
|
if (storage.blacklist && siteRules.length > 0) {
|
|
chrome.storage.sync.set({ siteRules: siteRules });
|
|
chrome.storage.sync.remove(["blacklist"]);
|
|
}
|
|
|
|
document.getElementById("siteRulesContainer").innerHTML = "";
|
|
if (siteRules.length > 0) {
|
|
siteRules.forEach((rule) => {
|
|
if (rule && rule.pattern) {
|
|
createSiteRule(rule);
|
|
}
|
|
});
|
|
}
|
|
|
|
var controllerButtons =
|
|
Array.isArray(storage.controllerButtons) &&
|
|
storage.controllerButtons.length > 0
|
|
? storage.controllerButtons
|
|
: tcDefaults.controllerButtons;
|
|
populateControlBarEditor(controllerButtons);
|
|
|
|
document.getElementById("popupMatchHoverControls").checked =
|
|
storage.popupMatchHoverControls !== false;
|
|
|
|
var popupButtons =
|
|
Array.isArray(storage.popupControllerButtons) &&
|
|
storage.popupControllerButtons.length > 0
|
|
? storage.popupControllerButtons
|
|
: tcDefaults.popupControllerButtons;
|
|
populatePopupControlBarEditor(popupButtons);
|
|
updatePopupEditorDisabledState();
|
|
});
|
|
}
|
|
|
|
function restore_defaults() {
|
|
document.querySelectorAll(".customs:not([id])").forEach((el) => el.remove());
|
|
|
|
chrome.storage.sync.set(tcDefaults, function () {
|
|
restore_options();
|
|
var status = document.getElementById("status");
|
|
status.textContent = "Default options restored";
|
|
setTimeout(function () {
|
|
status.textContent = "";
|
|
}, 1000);
|
|
});
|
|
}
|
|
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
var manifest = chrome.runtime.getManifest();
|
|
var versionElement = document.getElementById("app-version");
|
|
if (versionElement) {
|
|
versionElement.textContent = manifest.version;
|
|
}
|
|
|
|
restore_options();
|
|
initControlBarEditor();
|
|
|
|
document.getElementById("popupMatchHoverControls")
|
|
.addEventListener("change", updatePopupEditorDisabledState);
|
|
|
|
document.getElementById("save").addEventListener("click", save_options);
|
|
|
|
const addSelector = document.getElementById("addShortcutSelector");
|
|
if (addSelector) {
|
|
addSelector.addEventListener("change", function (e) {
|
|
if (e.target.value) {
|
|
add_shortcut(e.target.value);
|
|
e.target.value = ""; // Reset selector
|
|
}
|
|
});
|
|
}
|
|
document
|
|
.getElementById("restore")
|
|
.addEventListener("click", restore_defaults);
|
|
document
|
|
.getElementById("addSiteRule")
|
|
.addEventListener("click", function () {
|
|
createSiteRule(null);
|
|
});
|
|
|
|
function eventCaller(event, className, funcName) {
|
|
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) => {
|
|
if (event.target.classList.contains("removeParent")) {
|
|
event.target.parentNode.remove();
|
|
refreshAddShortcutSelector();
|
|
return;
|
|
}
|
|
if (event.target.classList.contains("remove-site-rule")) {
|
|
event.target.closest(".site-rule").remove();
|
|
return;
|
|
}
|
|
if (event.target.classList.contains("toggle-site-rule")) {
|
|
var ruleEl = event.target.closest(".site-rule");
|
|
var ruleBody = ruleEl.querySelector(".site-rule-body");
|
|
var isCollapsed = ruleEl.classList.contains("collapsed");
|
|
|
|
if (isCollapsed) {
|
|
ruleBody.style.display = "block";
|
|
ruleEl.classList.remove("collapsed");
|
|
event.target.textContent = "\u2212";
|
|
} else {
|
|
ruleBody.style.display = "none";
|
|
ruleEl.classList.add("collapsed");
|
|
event.target.textContent = "\u002b";
|
|
}
|
|
return;
|
|
}
|
|
});
|
|
document.addEventListener("change", (event) => {
|
|
if (event.target.classList.contains("customDo")) {
|
|
var valueInput = event.target.nextElementSibling.nextElementSibling;
|
|
if (customActionsNoValues.includes(event.target.value)) {
|
|
valueInput.disabled = true;
|
|
valueInput.value = 0;
|
|
} else {
|
|
valueInput.disabled = false;
|
|
}
|
|
}
|
|
|
|
// Handle site rule override checkboxes
|
|
if (event.target.classList.contains("override-shortcuts")) {
|
|
var container = event.target
|
|
.closest(".site-rule-shortcuts")
|
|
.querySelector(".site-shortcuts-container");
|
|
if (event.target.checked) {
|
|
container.style.display = "block";
|
|
if (container.children.length === 0) {
|
|
populateDefaultSiteShortcuts(container);
|
|
}
|
|
} else {
|
|
container.style.display = "none";
|
|
}
|
|
}
|
|
|
|
if (event.target.classList.contains("override-controlbar")) {
|
|
var cbContainer = event.target
|
|
.closest(".site-rule-controlbar")
|
|
.querySelector(".site-controlbar-container");
|
|
if (event.target.checked) {
|
|
cbContainer.style.display = "block";
|
|
var activeZone = cbContainer.querySelector(".site-cb-active");
|
|
if (activeZone && activeZone.children.length === 0) {
|
|
populateControlBarZones(
|
|
activeZone,
|
|
cbContainer.querySelector(".site-cb-available"),
|
|
getControlBarOrder()
|
|
);
|
|
}
|
|
} else {
|
|
cbContainer.style.display = "none";
|
|
}
|
|
}
|
|
|
|
if (event.target.classList.contains("override-popup-controlbar")) {
|
|
var popupCbContainer = event.target
|
|
.closest(".site-rule-controlbar")
|
|
.querySelector(".site-popup-controlbar-container");
|
|
if (event.target.checked) {
|
|
popupCbContainer.style.display = "block";
|
|
var popupActiveZone = popupCbContainer.querySelector(".site-popup-cb-active");
|
|
if (popupActiveZone && popupActiveZone.children.length === 0) {
|
|
populateControlBarZones(
|
|
popupActiveZone,
|
|
popupCbContainer.querySelector(".site-popup-cb-available"),
|
|
getPopupControlBarOrder()
|
|
);
|
|
}
|
|
} else {
|
|
popupCbContainer.style.display = "none";
|
|
}
|
|
}
|
|
});
|
|
});
|