GitHub Copy Code Snippet

A userscript adds a copy to clipboard button on hover of markdown code snippets

Fra 24.01.2018. Se den seneste versjonen.

  1. // ==UserScript==
  2. // @name GitHub Copy Code Snippet
  3. // @version 0.2.0
  4. // @description A userscript adds a copy to clipboard button on hover of markdown code snippets
  5. // @license MIT
  6. // @author Rob Garrison
  7. // @namespace https://github.com/Mottie
  8. // @include https://github.com/*
  9. // @run-at document-idle
  10. // @grant GM_addStyle
  11. // @require https://greatest.deepsurf.us/scripts/28721-mutations/code/mutations.js?version=234970
  12. // @icon https://assets-cdn.github.com/pinned-octocat.svg
  13. // ==/UserScript==
  14. (() => {
  15. "use strict";
  16.  
  17. const markdown = ".markdown-body, .markdown-format",
  18. code = `:any(${markdown}) pre:not(.gh-csc-pre)`,
  19.  
  20. copyButton = document.createElement("button");
  21.  
  22. addAttr(copyButton, {
  23. "aria-label": "Copy to clipboard",
  24. className: "js-zeroclipboard btn btn-sm zeroclipboard-button tooltipped tooltipped-w gh-csc-button",
  25. "data-copied-hint": "Copied!",
  26. type: "button",
  27. innerHTML: `
  28. <svg aria-hidden="true" class="octicon octicon-clippy" height="16" viewBox="0 0 14 16" width="14">
  29. <path fill-rule="evenodd" d="M2 13h4v1H2v-1zm5-6H2v1h5V7zm2 3V8l-3 3 3 3v-2h5v-2H9zM4.5 9H2v1h2.5V9zM2 12h2.5v-1H2v1zm9 1h1v2c-.02.28-.11.52-.3.7-.19.18-.42.28-.7.3H1c-.55 0-1-.45-1-1V4c0-.55.45-1 1-1h3c0-1.11.89-2 2-2 1.11 0 2 .89 2 2h3c.55 0 1 .45 1 1v5h-1V6H1v9h10v-2zM2 5h8c0-.55-.45-1-1-1H8c-.55 0-1-.45-1-1s-.45-1-1-1-1 .45-1 1-.45 1-1 1H3c-.55 0-1 .45-1 1z"></path>
  30. </svg>`
  31. });
  32.  
  33. GM_addStyle(`
  34. .gh-csc-wrap {
  35. position: relative;
  36. }
  37. .gh-csc-wrap:hover .gh-csc-button {
  38. display: block;
  39. }
  40. .gh-csc-button {
  41. display: none;
  42. position: absolute;
  43. top: 3px;
  44. right: 3px;
  45. }
  46. `);
  47.  
  48. // https://caniuse.com/#search=%3Aany
  49. function fixAnySelector(sel) {
  50. const prefix = document.head.style.MozOrient === "" ? "-moz-" : "-webkit-";
  51. return sel.replace(/:any\(/g, `:${prefix}any(`);
  52. }
  53.  
  54. function addAttr(el, attrs) {
  55. Object.keys(attrs).forEach(attr => {
  56. const value = attrs[attr];
  57. switch(attr) {
  58. case "className":
  59. el.className = value;
  60. break;
  61. case "innerHTML":
  62. el.innerHTML = value;
  63. break;
  64. default:
  65. el.setAttribute(attr, value);
  66. }
  67. });
  68. }
  69.  
  70. function addButton(wrap, code) {
  71. wrap.classList.add("gh-csc-wrap", "js-zeroclipboard-container");
  72. code.classList.add("gh-csc-code", "js-zeroclipboard-target");
  73. wrap.insertBefore(copyButton.cloneNode(true), wrap.childNodes[0]);
  74. }
  75.  
  76. function init() {
  77. if (document.querySelector(markdown)) {
  78. [...document.querySelectorAll(fixAnySelector(code))].forEach(pre => {
  79. let code = pre.firstElementChild;
  80. let wrap = pre.parentNode;
  81. if (code && code.nodeName === "CODE") {
  82. // pre > code
  83. addButton(pre, code);
  84. } else if (wrap.classList.contains("highlight")) {
  85. // div.highlight > pre
  86. addButton(wrap, pre);
  87. }
  88. });
  89. }
  90. }
  91.  
  92. document.addEventListener("ghmo:container", init);
  93. document.addEventListener("ghmo:comments", init);
  94. init();
  95. })();