diff --git a/Framework/Frontend/js/src/components/CopyToClipboardComponent.js b/Framework/Frontend/js/src/components/CopyToClipboardComponent.js index b8b5dd5e3..f2d12e2d6 100644 --- a/Framework/Frontend/js/src/components/CopyToClipboardComponent.js +++ b/Framework/Frontend/js/src/components/CopyToClipboardComponent.js @@ -14,7 +14,7 @@ import { StatefulComponent } from './StatefulComponent.js'; import { iconCheck, iconLinkIntact } from '../icons.js'; import { h } from '../renderer.js'; -import { isContextSecure } from '../utilities/browserContext.js'; +import { copyToClipboard, getClipboardUnavailableReason } from '../utilities/clipboard.js'; /** * Represents a component that allows copying text to the clipboard. @@ -27,15 +27,8 @@ export class CopyToClipboardComponent extends StatefulComponent { super(); this._successStateTimeout = null; - this._available = true; - this._message = ''; - - try { - this.checkClipboardAvailability(); - } catch ({ message: errorMessage }) { - this._available = false; - this._message = errorMessage; - } + this._message = getClipboardUnavailableReason() ?? ''; + this._available = !this._message; } /** @@ -47,7 +40,7 @@ export class CopyToClipboardComponent extends StatefulComponent { */ async copyToClipboard(clipboardTargetValue, onFailure) { try { - await navigator.clipboard.writeText(clipboardTargetValue); + await copyToClipboard(clipboardTargetValue); if (this._successStateTimeout) { clearTimeout(this._successStateTimeout); @@ -66,44 +59,6 @@ export class CopyToClipboardComponent extends StatefulComponent { } } - /** - * Checks the availability of the clipboard and provides a message if it is not accessible. - * - * @throws {Error} Throws an error with a descriptive message if the clipboard is not available. - * @returns {void} - */ - checkClipboardAvailability() { - if (!isContextSecure()) { - throw new Error('Clipboard not available in a non-secure context.'); - } - - if (!this.isClipboardSupported()) { - throw new Error('Clipboard API is not supported in this browser.'); - } - - if (this.isWindowEmbedded()) { - throw new Error('Clipboard access is restricted in iframes.'); - } - } - - /** - * Checks if the clipboard API is available in the user's browser. - * - * @returns {boolean} Returns `true` if it is available - */ - isClipboardSupported() { - return Boolean(navigator.clipboard); - } - - /** - * Check if the window is embedded in a frame. - * - * @returns {boolean} Returns `true` if it is embedded - */ - isWindowEmbedded() { - return window !== window.parent; - } - /** * Renders the button that allows copying text to the clipboard. * Attributes other than those listed are not forwarded to the button element. diff --git a/Framework/Frontend/js/src/index.js b/Framework/Frontend/js/src/index.js index 27391cf72..d4023c69a 100644 --- a/Framework/Frontend/js/src/index.js +++ b/Framework/Frontend/js/src/index.js @@ -27,6 +27,7 @@ export { documentClickTaggedEventRegistry } from './utilities/documentClickTagge export { buildUrl } from './utilities/buildUrl.js'; export { parseUrlParameters } from './utilities/parseUrlParameters.js'; export { isContextSecure } from './utilities/browserContext.js'; +export { copyToClipboard, getClipboardUnavailableReason } from './utilities/clipboard.js'; // Formatters export { formatTimeDuration } from './formatter/formatTimeDuration.js'; diff --git a/Framework/Frontend/js/src/utilities/clipboard.js b/Framework/Frontend/js/src/utilities/clipboard.js new file mode 100644 index 000000000..621810d6d --- /dev/null +++ b/Framework/Frontend/js/src/utilities/clipboard.js @@ -0,0 +1,52 @@ +/** + * @license + * Copyright 2019-2020 CERN and copyright holders of ALICE O2. + * See http://alice-o2.web.cern.ch/copyright for details of the copyright holders. + * All rights not expressly granted are reserved. + * + * This software is distributed under the terms of the GNU General Public + * License v3 (GPL Version 3), copied verbatim in the file "COPYING". + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +/* Global: window, navigator */ + +import { isContextSecure } from './browserContext.js'; + +/** + * Checks the availability of the clipboard and provides a message if it is not accessible. + * @returns {string|null} A descriptive reason if the clipboard is unavailable, `null` otherwise. + */ +export const getClipboardUnavailableReason = () => { + if (!isContextSecure()) { + return 'Clipboard not available in a non-secure context.'; + } + + if (!navigator.clipboard) { + return 'Clipboard API is not supported in this browser.'; + } + + if (window !== window.parent) { + return 'Clipboard access is restricted in iframes.'; + } + + return null; +}; + +/** + * Writes the given text to the user's clipboard. + * @param {string} value - The text to be copied to the clipboard. + * @returns {Promise} Resolves once the text has been written. + * @throws {Error} If the clipboard is unavailable or the write is rejected. + */ +export const copyToClipboard = async (value) => { + const unavailableReason = getClipboardUnavailableReason(); + if (unavailableReason) { + throw new Error(unavailableReason); + } + + await navigator.clipboard.writeText(value); +};