Element Fullscreen

Fullscreen any element on a webpage

当前为 2020-08-29 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Element Fullscreen
  3. // @namespace http://shitchell.com/
  4. // @include *
  5. // @description Fullscreen any element on a webpage
  6. // @author Shaun Mitchell <shaun@shitchell.com>
  7. // @license wtfpl
  8. // @grant GM_addStyle
  9. // @version 0.2
  10. // ==/UserScript==
  11.  
  12. // Send stuff to the console
  13. var DEBUG = false;
  14.  
  15. // Key combination to activate element selection (default is Alt-f)
  16. var toggleElementSelectionKey = "F";
  17. var toggleElementSelectionAlt = false;
  18. var toggleElementSelectionCtrl = true;
  19.  
  20. // Styles and css selectors
  21. var focusedStyle = `box-shadow: 0 3px 6px rgba(0,0,0,0.16),
  22. 0 3px 6px rgba(0,0,0,0.23),
  23. 0 3px 6px rgba(255,255,255,0.16),
  24. 0 3px 6px rgba(255,255,255,0.23) !important;`;
  25. var focusedSelector = "element-f";
  26. var fullScreenStyle = "padding: 1em !important;";
  27. var fullScreenSelector = "element-f-fullscreen";
  28.  
  29. // Element tracking
  30. var focusedElement = null;
  31.  
  32. // Start off not running until the defined keypress
  33. var running = false;
  34.  
  35. function debug()
  36. {
  37. if (DEBUG)
  38. {
  39. let args = Array.from(arguments);
  40. args.unshift("[Element-F]");
  41. console.log.apply(null, args);
  42. }
  43. }
  44.  
  45. /*
  46. * Returns a boolean that describes whether or not an element is fullscreened
  47. */
  48. function isFullScreen()
  49. {
  50. return document.fullScreen || document.mozFullScreen || document.webkitIsFullScreen;
  51. }
  52.  
  53. // Get the element directly under the mouse
  54. // https://stackoverflow.com/a/24540416/794241
  55. function getInnermostHovered()
  56. {
  57. return [].slice.call(document.querySelectorAll(':hover')).pop();
  58. }
  59.  
  60. /*
  61. * Removes any styling from any previously focused elements
  62. */
  63. function resetFocused()
  64. {
  65. debug("resetting any focused elements");
  66.  
  67. // Remove the focused class from any elements that have it
  68. document.querySelectorAll(`.${focusedSelector}`).forEach(function(el)
  69. {
  70. el.classList.remove(focusedSelector);
  71. debug("CLEARED: ", el);
  72. });
  73.  
  74. // Unset the focused element
  75. focusedElement = null;
  76. }
  77.  
  78. /*
  79. * Sets the currently hovered element as the focused element and
  80. * unsets any previously focused elements
  81. */
  82. function focusElement(el)
  83. {
  84. // Make sure we're running and the element isn't already focused
  85. if (!running || el === focusedElement)
  86. {
  87. return false;
  88. }
  89.  
  90. // Clear any previously focused elements
  91. resetFocused();
  92.  
  93. // Set the focus to this element
  94. focusedElement = el;
  95. debug("FOCUS: ", el);
  96.  
  97. // Add the hover class
  98. el.classList.add(focusedSelector);
  99. }
  100.  
  101. /*
  102. * Grabs the element under the cursor and sets it to focused
  103. */
  104. function setFocusedElement()
  105. {
  106. if (!running)
  107. {
  108. return false;
  109. }
  110.  
  111. let hoveredElement = getInnermostHovered();
  112. if (hoveredElement !== undefined)
  113. {
  114. focusElement(hoveredElement);
  115. }
  116. }
  117.  
  118. /*
  119. * Accepts an event from a listener and then fullscreens the target element
  120. */
  121. function fullScreenElement(ev)
  122. {
  123. if (!running)
  124. {
  125. return false;
  126. }
  127.  
  128. // Prevent whatever the event would have triggered (like following a link)
  129. ev.stopPropagation();
  130. ev.preventDefault();
  131.  
  132. if (ev.target !== null)
  133. {
  134. let req = ev.target.requestFullScreen || ev.target.webkitRequestFullScreen || ev.target.mozRequestFullScreen;
  135. if (req !== undefined)
  136. {
  137. // Fullscreen the target element
  138. req.call(ev.target);
  139.  
  140. // Add fullscreen class
  141. ev.target.classList.add(fullScreenSelector);
  142.  
  143. // Remove the fullscreen class after we're no longer fullscreened
  144. ev.target.addEventListener('fullscreenchange', function exitFullScreen() {
  145. if (!isFullScreen())
  146. {
  147. ev.target.classList.remove(fullScreenSelector);
  148. ev.target.removeEventListener('fullscreenchange', exitFullScreen);
  149. }
  150. });
  151.  
  152. // Make sure the target element has a background set
  153. ensureBackground(ev.target);
  154.  
  155. // Stop running
  156. running = false;
  157.  
  158. // Unset the target element as focused
  159. resetFocused();
  160. }
  161. }
  162. }
  163.  
  164. /*
  165. * Returns true if the specified key combination was pressed
  166. * to initiate element selection.
  167. */
  168. function validateKeyPress(ev)
  169. {
  170. if (ev.altKey != toggleElementSelectionAlt)
  171. {
  172. return false;
  173. }
  174. if (ev.ctrlKey != toggleElementSelectionCtrl)
  175. {
  176. return false;
  177. }
  178. if (ev.key != toggleElementSelectionKey)
  179. {
  180. return false;
  181. }
  182. debug("keypress triggered");
  183. return true;
  184. }
  185.  
  186. /*
  187. * Accepts a keypress event and then toggles running (ie, element selection mode)
  188. */
  189. function toggleRunning(ev)
  190. {
  191. if (validateKeyPress(ev)) {
  192. running = !running;
  193. debug("toggled running =>", running);
  194.  
  195. // Remove any focused elements if not running
  196. if (!running)
  197. {
  198. resetFocused();
  199. }
  200. }
  201. }
  202.  
  203. /*
  204. * Some elements are not set with a background color, defaulting to black in
  205. * fullscreen mode, which sometimes makes the text hard to read. This method
  206. * will check to see if an element lacks a background color and, if it does not,
  207. * temporarily gives it a black or white background based on its text color.
  208. */
  209. function ensureBackground(el)
  210. {
  211. debug("testing background for", el);
  212.  
  213. // First check to see that there isn't a background already
  214. let cS = getComputedStyle(el);
  215. let bgColor = cS.backgroundColor;
  216. if (bgColor == "rgba(0, 0, 0, 0)") // no background color is set
  217. {
  218. let textColor = getComputedStyle(el).color;
  219. textColor = textColor.substring(textColor.indexOf('(') +1, textColor.length -1).split(', ');
  220. textColor = {
  221. 'r': textColor[0],
  222. 'g': textColor[1],
  223. 'b': textColor[2]
  224. };
  225. bgColor = yiq(textColor.r, textColor.g, textColor.b);
  226.  
  227. // Set the background back to nothing after we exit fullscreen
  228. el.addEventListener('fullscreenchange', function removeBackground()
  229. {
  230. debug("potentially removing temporary background from", el);
  231. // Only run if the screen changed and exited fullscreen mode
  232. if (!isFullScreen())
  233. {
  234. debug("removing temporary background from", el);
  235. el.style.backgroundColor = null;
  236. el.removeEventListener('fullscreenchange', removeBackground);
  237. }
  238. });
  239. el.style.backgroundColor = bgColor;
  240. debug("YIQ: Got bg color", bgColor);
  241. }
  242. return bgColor;
  243. }
  244.  
  245. /*
  246. * Determines whether black or white is more appropriate for
  247. * a given color using YIQ computation
  248. */
  249. function yiq(r, g, b)
  250. {
  251. let color = Math.round(((parseInt(r) * 299) +
  252. (parseInt(g) * 587) +
  253. (parseInt(b) * 114)) / 1000);
  254. return (color > 125) ? 'black' : 'white';
  255. }
  256.  
  257. (function()
  258. {
  259. 'use strict';
  260.  
  261. // Set the style for the actively hovered element
  262. GM_addStyle(`.${focusedSelector} {
  263. cursor: crosshair !important;
  264. ${focusedStyle};
  265. }`);
  266.  
  267. // Set the style for the fullscreened element
  268. GM_addStyle(`.${fullScreenSelector} {
  269. overflow: auto !important;
  270. ${fullScreenStyle};
  271. }`);
  272.  
  273. // Toggle whether or not we're looking for elements based on the defined keypress
  274. document.body.addEventListener('keydown', toggleRunning);
  275.  
  276. // Set the element under the cursor to the focused element (only if running)
  277. document.body.addEventListener('mousemove', setFocusedElement);
  278.  
  279. // Listen for a click and fullscreen that element (only if running)
  280. document.body.addEventListener('click', fullScreenElement, true);
  281. })();