mirror of
				https://github.com/SoPat712/videospeed.git
				synced 2025-10-30 18:34:02 -04:00 
			
		
		
		
	Compare commits
	
		
			3 Commits
		
	
	
		
			v1.0.0
			...
			b7684aad09
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | b7684aad09 | ||
|   | 43dc8b773b | ||
|   | 2d8a4fc25f | 
| @@ -1,3 +1,7 @@ | ||||
| [](https://addons.mozilla.org/en-US/firefox/addon/video-speed-controller-v1/) | ||||
|  | ||||
|  | ||||
|  | ||||
| # The science of accelerated playback | ||||
|  | ||||
| **TL;DR: faster playback translates to better engagement and retention.** | ||||
| @@ -74,10 +78,10 @@ You can try manually disabling Flash from the browser. | ||||
| [`igrigorik/videospeed`](https://github.com/igrigorik/videospeed) repository | ||||
| is a port of [`igrigorik`](https://github.com/igrigorik)'s videospeed Chrome  | ||||
| add-on for Firefox. This fork modifies the Chrome add-on code so that it works  | ||||
| in Firefox. This repo is the code behind the [Firefox Extension](https://addons.mozilla.org/en-us/firefox/addon/videospeed/) | ||||
| in Firefox. This repo is the code behind the [Firefox Extension](https://addons.mozilla.org/en-US/firefox/addon/video-speed-controller-v1/) | ||||
| whereas the [`igrigorik/videospeed`](https://github.com/igrigorik/videospeed) | ||||
| repository contains the code behind the [Chrome Extension](https://chrome.google.com/webstore/detail/video-speed-controller/nffaoalbilbmmfgbnbgppjihopabppdk). | ||||
|  | ||||
| ### License | ||||
|  | ||||
| (MIT License) - Copyright (c) 2014 Ilya Grigorik | ||||
| (MIT License) - Copyright (c) 2014 Josh Patra | ||||
|   | ||||
| @@ -1,13 +1,13 @@ | ||||
| { | ||||
|   "name": "Video Speed Controller", | ||||
|   "short_name": "videospeed", | ||||
|   "version": "0.6.3.3", | ||||
|   "version": "1.1.1", | ||||
|   "manifest_version": 2, | ||||
|   "description": "Speed up, slow down, advance and rewind HTML5 audio/video with shortcuts", | ||||
|   "homepage_url": "https://github.com/codebicycle/videospeed", | ||||
|   "homepage_url": "https://github.com/SoPat712/videospeed", | ||||
|   "browser_specific_settings": { | ||||
|     "gecko": { | ||||
|       "id": "{7be2ba16-0f1e-4d93-9ebc-5164397477a9}" | ||||
|       "id": "{ed860648-f54f-4dc9-9a0d-501aec4313f5}" | ||||
|     } | ||||
|   }, | ||||
|   "icons": { | ||||
|   | ||||
							
								
								
									
										64
									
								
								options.html
									
									
									
									
									
								
							
							
						
						
									
										64
									
								
								options.html
									
									
									
									
									
								
							| @@ -1,4 +1,4 @@ | ||||
| <!DOCTYPE html> | ||||
| <!doctype html> | ||||
| <html> | ||||
|   <head> | ||||
|     <title>Video Speed Controller: Options</title> | ||||
| @@ -148,8 +148,13 @@ | ||||
|         <input id="rememberSpeed" type="checkbox" /> | ||||
|       </div> | ||||
|       <div class="row"> | ||||
|         <label for="forceLastSavedSpeed">Force last saved speed<br /> | ||||
|         <em>Useful for video players that override the speeds set by VideoSpeed</em></label> | ||||
|         <label for="forceLastSavedSpeed" | ||||
|           >Force last saved speed<br /> | ||||
|           <em | ||||
|             >Useful for video players that override the speeds set by | ||||
|             VideoSpeed</em | ||||
|           ></label | ||||
|         > | ||||
|         <input id="forceLastSavedSpeed" type="checkbox" /> | ||||
|       </div> | ||||
|       <div class="row"> | ||||
| @@ -175,6 +180,47 @@ | ||||
|       </div> | ||||
|     </section> | ||||
|  | ||||
|     <section id="nudgeSettings"> | ||||
|       <h3>Subtitle Nudge Settings (Experimental - YouTube Only)</h3> | ||||
|       <div class="row"> | ||||
|         <label for="enableSubtitleNudge" | ||||
|           >Enable Subtitle Nudge <br /><em | ||||
|             >Periodically 'nudges' video speed by a tiny amount to help keep | ||||
|             subtitles in sync on some sites (e.g. YouTube).</em | ||||
|           > | ||||
|         </label> | ||||
|         <input id="enableSubtitleNudge" type="checkbox" /> | ||||
|       </div> | ||||
|       <div class="row"> | ||||
|         <label for="subtitleNudgeInterval" | ||||
|           >Nudge Interval (milliseconds) <br /><em | ||||
|             >How often to nudge (e.g., 25-1000). Smaller values are more | ||||
|             frequent. Default: 25.</em | ||||
|           > | ||||
|         </label> | ||||
|         <input | ||||
|           id="subtitleNudgeInterval" | ||||
|           type="text" | ||||
|           value="" | ||||
|           placeholder="25" | ||||
|         /> | ||||
|       </div> | ||||
|       <div class="row"> | ||||
|         <label for="subtitleNudgeAmount" | ||||
|           >Nudge Amount (decimal) <br /><em | ||||
|             >How much to change speed by (e.g., 0.001). Very small values | ||||
|             recommended. Default: 0.001.</em | ||||
|           > | ||||
|         </label> | ||||
|         <input | ||||
|           id="subtitleNudgeAmount" | ||||
|           type="text" | ||||
|           value="" | ||||
|           placeholder="0.001" | ||||
|         /> | ||||
|       </div> | ||||
|     </section> | ||||
|  | ||||
|     <button id="save">Save</button> | ||||
|     <button id="restore">Restore Defaults</button> | ||||
|     <button id="experimental">Show Experimental Features</button> | ||||
| @@ -186,12 +232,12 @@ | ||||
|  | ||||
|       <h4>Extension controls not appearing?</h4> | ||||
|       <p> | ||||
|         This extension is only compatible with HTML5 audio and video. If you don't | ||||
|         see the controls showing up, chances are you are viewing a Flash content. | ||||
|         If you want to confirm, try right-clicking on the content and inspect the | ||||
|         menu: if it mentions flash, then that's the issue. That said, <b>most sites | ||||
|         will fallback to HTML5</b> if they detect that Flash is not available. You | ||||
|         can try manually disabling Flash from the browser. | ||||
|         This extension is only compatible with HTML5 audio and video. If you | ||||
|         don't see the controls showing up, chances are you are viewing a Flash | ||||
|         content. If you want to confirm, try right-clicking on the content and | ||||
|         inspect the menu: if it mentions flash, then that's the issue. That | ||||
|         said, <b>most sites will fallback to HTML5</b> if they detect that Flash | ||||
|         is not available. You can try manually disabling Flash from the browser. | ||||
|       </p> | ||||
|     </div> | ||||
|   </body> | ||||
|   | ||||
							
								
								
									
										278
									
								
								options.js
									
									
									
									
									
								
							
							
						
						
									
										278
									
								
								options.js
									
									
									
									
									
								
							| @@ -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; | ||||
|       } | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								videospeed.xpi
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								videospeed.xpi
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
		Reference in New Issue
	
	Block a user