/** * Captures any pasted data and sends it to method "receivedCopyPasteData" which has to be * defined on the component which uses this mixin */ import Vue from 'vue'; import { debounce } from 'lodash'; export const copyPaste = Vue.extend({ data () { return { copyPasteElementsGotCreated: false, }; }, mounted () { if (this.copyPasteElementsGotCreated === true) { return; } this.copyPasteElementsGotCreated = true; // Define the style of the html elements that get created to make // sure that they are not visible const style = document.createElement('style'); style.type = 'text/css'; style.innerHTML = ` .hidden-copy-paste { position: absolute; bottom: 0; left: 0; width: 10px; height: 10px; display: block; font-size: 1px; z-index: -1; color: transparent; background: transparent; overflow: hidden; border: none; padding: 0; resize: none; outline: none; -webkit-user-select: text; user-select: text; } `; document.getElementsByTagName('head')[0].appendChild(style); // Code is mainly from // https://www.lucidchart.com/techblog/2014/12/02/definitive-guide-copying-pasting-javascript/ const isSafari = navigator.appVersion.search('Safari') !== -1 && navigator.appVersion.search('Chrome') === -1 && navigator.appVersion.search('CrMo') === -1 && navigator.appVersion.search('CriOS') === -1; const isIe = (navigator.userAgent.toLowerCase().indexOf('msie') !== -1 || navigator.userAgent.toLowerCase().indexOf('trident') !== -1); const hiddenInput = document.createElement('input'); hiddenInput.setAttribute('type', 'text'); hiddenInput.setAttribute('id', 'hidden-input-copy-paste'); hiddenInput.setAttribute('class', 'hidden-copy-paste'); document.body.append(hiddenInput); let ieClipboardDiv: HTMLDivElement | null = null; if (isIe) { ieClipboardDiv = document.createElement('div'); ieClipboardDiv.setAttribute('id', 'hidden-ie-clipboard-copy-paste'); ieClipboardDiv.setAttribute('class', 'hidden-copy-paste'); ieClipboardDiv.setAttribute('contenteditable', 'true'); document.body.append(ieClipboardDiv); document.addEventListener('beforepaste', () => { // @ts-ignore if (hiddenInput.is(':focus')) { this.focusIeClipboardDiv(ieClipboardDiv as HTMLDivElement); } }, true); } let userInput = ''; const hiddenInputListener = (text: string) => { }; hiddenInput.addEventListener('input', (e) => { const value = hiddenInput.value; userInput += value; hiddenInputListener(userInput); // There is a bug (sometimes) with Safari and the input area can't be updated during // the input event, so we update the input area after the event is done being processed if (isSafari) { hiddenInput.focus(); setTimeout(() => { this.focusHiddenArea(hiddenInput); }, 0); } else { this.focusHiddenArea(hiddenInput); } }); // Set clipboard event listeners on the document. ['paste'].forEach((event) => { document.addEventListener(event, debounce((e) => { // Check if the event got emitted from a message box or from something // else which should ignore the copy/paste // @ts-ignore const path = e.path || (e.composedPath && e.composedPath()); for (let index = 0; index < path.length; index++) { if (path[index].className && typeof path[index].className === 'string' && ( path[index].className.includes('el-message-box') || path[index].className.includes('ignore-key-press') )) { return; } } if (ieClipboardDiv !== null) { this.ieClipboardEvent(event, ieClipboardDiv); } else { this.standardClipboardEvent(event, e as ClipboardEvent); // @ts-ignore if (!document.activeElement || (document.activeElement && ['textarea', 'text', 'email', 'password'].indexOf(document.activeElement.type) === -1)) { // That it still allows to paste into text, email, password & textarea-fiels we // check if we can identify the active element and if so only // run it if something else is selected. this.focusHiddenArea(hiddenInput); e.preventDefault(); } } }, 1000, { leading: true })); }); }, methods: { receivedCopyPasteData (plainTextData: string, event?: ClipboardEvent): void { // THIS HAS TO BE DEFINED IN COMPONENT! }, // For every browser except IE, we can easily get and set data on the clipboard standardClipboardEvent (clipboardEventName: string, event: ClipboardEvent) { const clipboardData = event.clipboardData; if (clipboardData !== null && clipboardEventName === 'paste') { const clipboardText = clipboardData.getData('text/plain'); this.receivedCopyPasteData(clipboardText, event); } }, // For IE, we can get/set Text or URL just as we normally would ieClipboardEvent (clipboardEventName: string, ieClipboardDiv: HTMLDivElement) { // @ts-ignore const clipboardData = window.clipboardData; if (clipboardEventName === 'paste') { const clipboardText = clipboardData.getData('Text'); // @ts-ignore ieClipboardDiv.empty(); this.receivedCopyPasteData(clipboardText); } }, // Focuses an element to be ready for copy/paste (used exclusively for IE) focusIeClipboardDiv (ieClipboardDiv: HTMLDivElement) { ieClipboardDiv.focus(); const range = document.createRange(); // @ts-ignore range.selectNodeContents((ieClipboardDiv.get(0))); const selection = window.getSelection(); if (selection !== null) { selection.removeAllRanges(); selection.addRange(range); } }, focusHiddenArea (hiddenInput: HTMLInputElement) { // In order to ensure that the browser will fire clipboard events, we always need to have something selected hiddenInput.value = ' '; hiddenInput.focus(); hiddenInput.select(); }, /** * Copies given data to clipboard */ copyToClipboard (value: string): void { // FROM: https://hackernoon.com/copying-text-to-clipboard-with-javascript-df4d4988697f const element = document.createElement('textarea'); // Create a