|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- 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, menuItemElements;
-
- function hideMenu() {
- if (!menuEl) return;
- menuEl.parentNode.removeChild(menuEl);
- menuEl = undefined;
- document.body.removeEventListener('keydown', handleKeyDown);
- }
-
- 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
- ? 'copySelectionContextMenuItemForFragment'
- : 'copySelectionContextMenuItemForSingleMoment'
- ),
- async action() {
- await navigator.clipboard.writeText(preciseUrl());
- },
- },
- {
- title: browser.i18n.getMessage(end !== undefined
- ? 'bookmarkSelectionContextMenuItemForFragment'
- : 'bookmarkSelectionContextMenuItemForSingleMoment'
- ),
- async action() {
- await remoteFunction('createBookmark')({ url: preciseUrl(), start, end, trackDuration });
- },
- },
- ];
-
- menuItemElements = menuItems.map(({ title, action }) => html`
- <li>
- <button onclick=${event => { hideMenu(); action(event); }}>
- ${title}
- </button>
- </li>
- `);
-
- hideMenu();
-
- menuEl = html`
- <ul class="context-menu" style="top: ${top}; left: ${left};">
- ${menuItemElements}
- </ul>
- `;
-
- document.body.appendChild(menuEl);
-
- document.body.addEventListener('keydown', handleKeyDown);
- }
-
- function handleKeyDown(event) {
- if (event.code === 'Escape') {
- hideMenu();
- return;
- }
-
- const currentIndex = menuItemElements.findIndex(el => el.querySelector('button:focus'));
- if (event.code === 'ArrowDown') {
- const newIndex = (currentIndex === -1 || currentIndex === menuItemElements.length - 1)
- ? 0
- : currentIndex + 1;
- menuItemElements[newIndex].querySelector('button').focus();
- }
- if (event.code === 'ArrowUp') {
- const newIndex = (currentIndex === -1 || currentIndex === 0)
- ? menuItemElements.length - 1
- : currentIndex - 1;
- menuItemElements[newIndex].querySelector('button').focus();
- }
- }
-
- 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);
- }
|