contentscript.js 2.5 KiB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. import html from 'nanohtml';
  2. import { remoteFunction } from 'webextension-rpc';
  3. import { createMediaFragmentIdentifier } from '../util/media-fragment-identifier';
  4. function createPreciseUrl(url, selector) {
  5. const properUrl = url.split('#')[0];
  6. let fragmentIdentifier;
  7. if (selector.type === 'FragmentSelector') {
  8. fragmentIdentifier = selector.value;
  9. } else {
  10. throw new Error('Unsupported selector type');
  11. }
  12. return properUrl + '#' + fragmentIdentifier;
  13. }
  14. function describeMediaFragment({ start, end }) {
  15. const fragmentIdentifier = createMediaFragmentIdentifier({ start, end });
  16. const selector = {
  17. type: 'FragmentSelector',
  18. conformsTo: 'http://www.w3.org/TR/media-frags/',
  19. value: fragmentIdentifier,
  20. };
  21. return selector;
  22. }
  23. export default async function init(playlist) {
  24. let menuEl;
  25. function hideMenu() {
  26. if (!menuEl) return;
  27. menuEl.parentNode.removeChild(menuEl);
  28. menuEl = undefined;
  29. }
  30. function onContextMenu(event) {
  31. event.preventDefault();
  32. const left = event.pageX;
  33. const top = event.pageY;
  34. const trackDuration = playlist.duration;
  35. let { start, end } = playlist.getTimeSelection();
  36. if (start === undefined) return;
  37. // Waveform Playlist tells us end=start when the selection is just a single line.
  38. if (end === start) end = undefined;
  39. // Round the numbers to two decimals.
  40. // Also, as Waveform Playlist might report a slightly negative number, first cap it to zero.
  41. start = Math.round(Math.max(0, start) * 100) / 100;
  42. if (end !== undefined) end = Math.round(Math.max(0, end) * 100) / 100;
  43. async function createBookmark() {
  44. const fileUrl = document.URL;
  45. const selector = describeMediaFragment({ start, end });
  46. const bookmarkUrl = createPreciseUrl(fileUrl, selector);
  47. await remoteFunction('createBookmark')({ url: bookmarkUrl, start, end, trackDuration });
  48. }
  49. hideMenu();
  50. menuEl = html`
  51. <ul class="context-menu" style="top: ${top}; left: ${left};">
  52. <li>
  53. <button onclick=${() => { hideMenu(); createBookmark(); }}>
  54. ${browser.i18n.getMessage(end !== undefined
  55. ? 'bookmarkSelectionContextMenuItemForFragment'
  56. : 'bookmarkSelectionContextMenuItemForSingleMoment'
  57. )}
  58. </button>
  59. </li>
  60. </ul>
  61. `;
  62. document.body.appendChild(menuEl);
  63. }
  64. function onMouseDown(event) {
  65. // A mouse-down outside the menu hides the menu.
  66. if (menuEl && !menuEl.contains(event.target)) { // note nodeX.contains(nodeX) === true
  67. hideMenu();
  68. }
  69. }
  70. document.addEventListener('contextmenu', onContextMenu);
  71. document.addEventListener('mousedown', onMouseDown);
  72. }