Draggable Reference Line

Designed for reading long articles, it helps remember the current reading progress.

  1. // ==UserScript==
  2. // @name Draggable Reference Line
  3. // @namespace https://greasyfork.org/users/193469
  4. // @description Designed for reading long articles, it helps remember the current reading progress.
  5. // @license MIT
  6. // @version 1.1.2
  7. // @author Rui LIU (@liurui39660)
  8. // @match *://*/*
  9. // @icon https://icons.duckduckgo.com/ip2/example.com.ico
  10. // @run-at document-body
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15.  
  16. const height = 2; // px, height of the visible line
  17. const padding = 2; // px, additional draggable area above and below the line, not the sum of them
  18. const color = 'red'; // or #-string
  19.  
  20. const body = document.getElementsByTagName('body')[0];
  21. const line = document.createElement('div');
  22. const initPos = -padding - height / 2;
  23. line.textContent = '#DraggableReferenceLine';
  24. line.style = `margin: 0; padding: ${padding}px 0; cursor: n-resize; font-size: 0; color: #fff0; background: ${color}; background-clip: content-box; box-sizing: content-box; width: 100%; height: ${height}px; position: absolute; top: ${(localStorage[`DraggableLine_${window.location.pathname}${window.location.search}`] || initPos)}px; z-index: 2147483647;`;
  25.  
  26. const reset = () => {
  27. line.style.top = `${initPos}px`;
  28. localStorage.removeItem(`DraggableLine_${window.location.pathname}${window.location.search}`);
  29. }
  30. let callbackMouseMove = ev => {
  31. line.style.top = `${ev.pageY + initPos}px`; // initPos is also the offset to line center
  32. };
  33. let callbackMouseUp = ev => {
  34. if (ev.detail === 1) { // Don't overwrite dblclick
  35. body.style.cursor = null;
  36. const top = parseFloat(line.style.top);
  37. top <= initPos ? reset() : localStorage[`DraggableLine_${window.location.pathname}${window.location.search}`] = top;
  38. [onmouseup, onmousemove, callbackMouseUp, callbackMouseMove] = [callbackMouseUp, callbackMouseMove, onmouseup, onmousemove];
  39. }
  40. };
  41. line.addEventListener('mousedown', ev => {
  42. if (ev.detail === 1) {
  43. ev.preventDefault(); // Don't select text
  44. body.style.cursor = 'n-resize'; // I know setPointerCapture, but see https://stackoverflow.com/questions/57566090/setpointercapture-behaves-differently-in-chrome-and-firefox
  45. [onmouseup, onmousemove, callbackMouseUp, callbackMouseMove] = [callbackMouseUp, callbackMouseMove, onmouseup, onmousemove];
  46. }
  47. });
  48. line.addEventListener('dblclick', reset);
  49.  
  50. body.appendChild(line);
  51. })();