import html from 'nanohtml'; import { remoteFunction } from 'webextension-rpc'; import { createMediaFragmentIdentifier } from '../util/media-fragment-identifier'; function createPreciseUrl(url, selector) { const properUrl = url.split('#')[0]; let fragmentIdentifier; if (selector.type === 'FragmentSelector') { fragmentIdentifier = selector.value; } else { throw new Error('Unsupported selector type'); } return properUrl + '#' + fragmentIdentifier; } function describeMediaFragment({ start, end }) { const fragmentIdentifier = createMediaFragmentIdentifier({ start, end }); const selector = { type: 'FragmentSelector', conformsTo: 'http://www.w3.org/TR/media-frags/', value: fragmentIdentifier, }; return selector; } export default async function init(playlist) { let menuEl; function hideMenu() { if (!menuEl) return; menuEl.parentNode.removeChild(menuEl); menuEl = undefined; } function onContextMenu(event) { event.preventDefault(); const left = event.pageX; const top = event.pageY; const trackDuration = playlist.duration; let { start, end } = playlist.getTimeSelection(); if (start === undefined) return; // Waveform Playlist tells us end=start when the selection is just a single line. if (end === start) end = undefined; // Round the numbers to two decimals. // Also, as Waveform Playlist might report a slightly negative number, first cap it to zero. start = Math.round(Math.max(0, start) * 100) / 100; if (end !== undefined) end = Math.round(Math.max(0, end) * 100) / 100; function preciseUrl() { const fileUrl = document.URL; const selector = describeMediaFragment({ start, end }); const preciseUrl = createPreciseUrl(fileUrl, selector); return preciseUrl; } const menuItems = [ { title: browser.i18n.getMessage(end !== undefined ? 'bookmarkSelectionContextMenuItemForFragment' : 'bookmarkSelectionContextMenuItemForSingleMoment' ), async action() { await remoteFunction('createBookmark')({ url: preciseUrl(), start, end, trackDuration }); }, }, ]; hideMenu(); menuEl = html` `; document.body.appendChild(menuEl); } function onMouseDown(event) { // A mouse-down outside the menu hides the menu. if (menuEl && !menuEl.contains(event.target)) { // note nodeX.contains(nodeX) === true hideMenu(); } } document.addEventListener('contextmenu', onContextMenu); document.addEventListener('mousedown', onMouseDown); }