All files / app/assets/javascripts/behaviors copy_to_clipboard.js

72.73% Statements 32/44
50% Branches 7/14
87.5% Functions 7/8
76.19% Lines 32/42

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112              123x 123x 123x     6x   6x 4x 4x 4x 4x       6x 6x 6x 6x 6x 6x           5x 5x 5x   5x 5x   3x                 4x   4x 4x 3x         9x 9x 9x                       9x                                         9x                       2x   2x        
import ClipboardJS from 'clipboard';
import $ from 'jquery';
 
import { parseBoolean } from '~/lib/utils/common_utils';
import { __ } from '~/locale';
import { fixTitle, add, show, hide, once } from '~/tooltips';
 
const CLIPBOARD_SUCCESS_EVENT = 'clipboard-success';
const CLIPBOARD_ERROR_EVENT = 'clipboard-error';
const I18N_ERROR_MESSAGE = __('Copy failed. Please manually copy the value.');
 
function showTooltip(target, title) {
  const { title: originalTitle } = target.dataset;
 
  once('hidden', (tooltip) => {
    Eif (tooltip.target === target) {
      target.setAttribute('title', originalTitle);
      target.setAttribute('aria-label', originalTitle);
      fixTitle(target);
    }
  });
 
  target.setAttribute('title', title);
  target.setAttribute('aria-label', title);
  fixTitle(target);
  show(target);
  setTimeout(() => {
    hide(target);
  }, 1000);
}
 
function genericSuccess(e) {
  // Clear the selection
  e.clearSelection();
  e.trigger.focus();
  e.trigger.dispatchEvent(new Event(CLIPBOARD_SUCCESS_EVENT));
 
  const { clipboardHandleTooltip = true } = e.trigger.dataset;
  if (parseBoolean(clipboardHandleTooltip)) {
    // Update tooltip
    showTooltip(e.trigger, __('Copied'));
  }
}
 
/**
 * Safari > 10 doesn't support `execCommand`, so instead we inform the user to copy manually.
 * See http://clipboardjs.com/#browser-support
 */
function genericError(e) {
  e.trigger.dispatchEvent(new Event(CLIPBOARD_ERROR_EVENT));
 
  const { clipboardHandleTooltip = true } = e.trigger.dataset;
  if (parseBoolean(clipboardHandleTooltip)) {
    showTooltip(e.trigger, I18N_ERROR_MESSAGE);
  }
}
 
export default function initCopyToClipboard() {
  const clipboard = new ClipboardJS('[data-clipboard-target], [data-clipboard-text]');
  clipboard.on('success', genericSuccess);
  clipboard.on('error', genericError);
 
  /**
   * This a workaround around ClipboardJS limitations to allow the context-specific copy/pasting
   * of plain text or GFM. The Ruby `clipboard_button` helper sneaks a JSON hash with `text` and
   * `gfm` keys into the `data-clipboard-text` attribute that ClipboardJS reads from.
   * When ClipboardJS creates a new `textarea` (directly inside `body`, with a `readonly`
   * attribute`), sets its value to the value of this data attribute, focusses on it, and finally
   * programmatically issues the 'Copy' command, this code intercepts the copy command/event at
   * the last minute to deconstruct this JSON hash and set the `text/plain` and `text/x-gfm` copy
   * data types to the intended values.
   */
  $(document).on('copy', 'body > textarea[readonly]', (e) => {
    const { clipboardData } = e.originalEvent;
    if (!clipboardData) return;
 
    const text = e.target.value;
 
    let json;
    try {
      json = JSON.parse(text);
    } catch (ex) {
      return;
    }
 
    if (!json.text || !json.gfm) return;
 
    e.preventDefault();
 
    clipboardData.setData('text/plain', json.text);
    clipboardData.setData('text/x-gfm', json.gfm);
  });
 
  return clipboard;
}
 
/**
 * Programmatically triggers a click event on a
 * "copy to clipboard" button, causing its
 * contents to be copied. Handles some of the messiniess
 * around managing the button's tooltip.
 * @param {HTMLElement} btnElement
 */
export function clickCopyToClipboardButton(btnElement) {
  // Ensure the button has already been tooltip'd.
  add([btnElement], { show: true });
 
  btnElement.click();
}
 
export { CLIPBOARD_SUCCESS_EVENT, CLIPBOARD_ERROR_EVENT, I18N_ERROR_MESSAGE };