121 lines
3.4 KiB
TypeScript
121 lines
3.4 KiB
TypeScript
import deselectCurrent from './toggle-selection';
|
|
|
|
interface Options {
|
|
debug?: boolean;
|
|
message?: string;
|
|
format?: string; // MIME type
|
|
onCopy?: (clipboardData: object) => void;
|
|
}
|
|
|
|
const clipboardToIE11Formatting = {
|
|
'text/plain': 'Text',
|
|
'text/html': 'Url',
|
|
default: 'Text',
|
|
};
|
|
|
|
const defaultMessage = 'Copy to clipboard: #{key}, Enter';
|
|
|
|
function format(message: string) {
|
|
const copyKey = (/mac os x/i.test(navigator.userAgent) ? '⌘' : 'Ctrl') + '+C';
|
|
return message.replace(/#{\s*key\s*}/g, copyKey);
|
|
}
|
|
|
|
function copy(text: string, options?: Options): boolean {
|
|
let message,
|
|
reselectPrevious,
|
|
range,
|
|
selection,
|
|
mark,
|
|
success = false;
|
|
if (!options) {
|
|
options = {};
|
|
}
|
|
const debug = options.debug || false;
|
|
try {
|
|
reselectPrevious = deselectCurrent();
|
|
|
|
range = document.createRange();
|
|
selection = document.getSelection();
|
|
|
|
mark = document.createElement('span');
|
|
mark.textContent = text;
|
|
// reset user styles for span element
|
|
mark.style.all = 'unset';
|
|
// prevents scrolling to the end of the page
|
|
mark.style.position = 'fixed';
|
|
mark.style.top = 0;
|
|
mark.style.clip = 'rect(0, 0, 0, 0)';
|
|
// used to preserve spaces and line breaks
|
|
mark.style.whiteSpace = 'pre';
|
|
// do not inherit user-select (it may be `none`)
|
|
mark.style.webkitUserSelect = 'text';
|
|
mark.style.MozUserSelect = 'text';
|
|
mark.style.msUserSelect = 'text';
|
|
mark.style.userSelect = 'text';
|
|
mark.addEventListener('copy', function(e) {
|
|
e.stopPropagation();
|
|
if (options.format) {
|
|
e.preventDefault();
|
|
if (typeof e.clipboardData === 'undefined') {
|
|
// IE 11
|
|
debug && console.warn('unable to use e.clipboardData');
|
|
debug && console.warn('trying IE specific stuff');
|
|
(window as any).clipboardData.clearData();
|
|
const format =
|
|
clipboardToIE11Formatting[options.format] || clipboardToIE11Formatting['default'];
|
|
(window as any).clipboardData.setData(format, text);
|
|
} else {
|
|
// all other browsers
|
|
e.clipboardData.clearData();
|
|
e.clipboardData.setData(options.format, text);
|
|
}
|
|
}
|
|
if (options.onCopy) {
|
|
e.preventDefault();
|
|
options.onCopy(e.clipboardData);
|
|
}
|
|
});
|
|
|
|
document.body.appendChild(mark);
|
|
|
|
range.selectNodeContents(mark);
|
|
selection.addRange(range);
|
|
|
|
const successful = document.execCommand('copy');
|
|
if (!successful) {
|
|
throw new Error('copy command was unsuccessful');
|
|
}
|
|
success = true;
|
|
} catch (err) {
|
|
debug && console.error('unable to copy using execCommand: ', err);
|
|
debug && console.warn('trying IE specific stuff');
|
|
try {
|
|
(window as any).clipboardData.setData(options.format || 'text', text);
|
|
options.onCopy && options.onCopy((window as any).clipboardData);
|
|
success = true;
|
|
} catch (err) {
|
|
debug && console.error('unable to copy using clipboardData: ', err);
|
|
debug && console.error('falling back to prompt');
|
|
message = format('message' in options ? options.message : defaultMessage);
|
|
window.prompt(message, text);
|
|
}
|
|
} finally {
|
|
if (selection) {
|
|
if (typeof selection.removeRange == 'function') {
|
|
selection.removeRange(range);
|
|
} else {
|
|
selection.removeAllRanges();
|
|
}
|
|
}
|
|
|
|
if (mark) {
|
|
document.body.removeChild(mark);
|
|
}
|
|
reselectPrevious();
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
export default copy;
|