From e78f375e8185dafe007147d1f6b8ac1611fe885c Mon Sep 17 00:00:00 2001 From: Yunjie Chen Date: Sat, 8 Jun 2019 12:10:40 +0800 Subject: [PATCH] markdown: add copy button to code blocks to copy source text --- src/resources/common.css | 37 +++++++++++++++++++++++++ src/resources/hoedown.js | 1 + src/resources/markdown-it.js | 1 + src/resources/markdown_template.html | 1 + src/resources/markdown_template.js | 24 +++++++++++++++- src/resources/marked.js | 1 + src/resources/showdown.js | 1 + src/utils/clipboard.js/README.md | 2 ++ src/utils/clipboard.js/clipboard.min.js | 7 +++++ src/utils/vutils.cpp | 12 ++++++++ src/vnote.qrc | 1 + 11 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 src/utils/clipboard.js/README.md create mode 100644 src/utils/clipboard.js/clipboard.min.js diff --git a/src/resources/common.css b/src/resources/common.css index 9551995b..6518f21d 100644 --- a/src/resources/common.css +++ b/src/resources/common.css @@ -208,3 +208,40 @@ span.modal-close:focus { content: attr(data-anchor-icon); } /* Anchor */ + +/* Button */ +.vnote-btn { + position: relative; + display: inline-block; + padding: 6px 12px; + font-size: 13px; + font-weight: 700; + line-height: 20px; + white-space: nowrap; + vertical-align: middle; + cursor: pointer; + border: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-appearance: none; +} + +.vnote-copy-clipboard-btn { + transition: opacity .3s ease-in-out; + opacity: 0; + padding: 2px 6px; + position: absolute; + top: 5px; + right: 5px; +} + +pre:hover .vnote-copy-clipboard-btn { + opacity: 1; +} + +pre.vnote-snippet { + position: relative; +} +/* Button */ diff --git a/src/resources/hoedown.js b/src/resources/hoedown.js index 1ac593f7..308e8612 100644 --- a/src/resources/hoedown.js +++ b/src/resources/hoedown.js @@ -91,6 +91,7 @@ var updateHtml = function(html) { } addClassToCodeBlock(); + addCopyButtonToCodeBlock(); renderCodeBlockLineNumber(); // If you add new logics after handling MathJax, please pay attention to diff --git a/src/resources/markdown-it.js b/src/resources/markdown-it.js index 58eff9da..0435c361 100644 --- a/src/resources/markdown-it.js +++ b/src/resources/markdown-it.js @@ -175,6 +175,7 @@ var updateText = function(text) { renderPlantUML('lang-puml'); renderGraphviz('lang-dot'); addClassToCodeBlock(); + addCopyButtonToCodeBlock(); renderCodeBlockLineNumber(); // If you add new logics after handling MathJax, please pay attention to diff --git a/src/resources/markdown_template.html b/src/resources/markdown_template.html index 4e2f36b2..0850bb3d 100644 --- a/src/resources/markdown_template.html +++ b/src/resources/markdown_template.html @@ -27,6 +27,7 @@ + diff --git a/src/resources/markdown_template.js b/src/resources/markdown_template.js index ea1cde5b..15355043 100644 --- a/src/resources/markdown_template.js +++ b/src/resources/markdown_template.js @@ -23,6 +23,8 @@ var VPlantUMLDivClass = 'plantuml-diagram'; var VMetaDataCodeClass = 'markdown-metadata'; var VMarkRectDivClass = 'mark-rect'; +var hljsClass = 'hljs'; + var VPreviewMode = false; if (typeof VEnableMermaid == 'undefined') { @@ -272,6 +274,10 @@ window.onwheel = function(e) { var skipScrollCheckRange = null; +window.addEventListener('load', function() { + new ClipboardJS('.vnote-copy-clipboard-btn'); +}); + window.onscroll = function() { if (g_muteScroll) { return; @@ -1247,7 +1253,6 @@ var renderCodeBlockLineNumber = function() { }; var addClassToCodeBlock = function() { - var hljsClass = 'hljs'; var codes = document.getElementsByTagName('code'); for (var i = 0; i < codes.length; ++i) { var code = codes[i]; @@ -1267,6 +1272,23 @@ var addClassToCodeBlock = function() { } }; +var addCopyButtonToCodeBlock = function() { + var codes = document.getElementsByClassName(hljsClass); + for (var i = 0; i < codes.length; ++i) { + var code = codes[i]; + var pare = code.parentElement; + pare.classList.add('vnote-snippet'); + + var btn = document.createElement('button'); + btn.innerHTML = '📋'; + btn.classList.add('vnote-btn'); + btn.classList.add('vnote-copy-clipboard-btn'); + btn.setAttribute('type', 'button'); + btn.setAttribute('data-clipboard-text', code.textContent); + code.insertAdjacentElement('beforebegin', btn); + } +}; + var listContainsRegex = function(strs, exp) { for (var i = 0, len = strs.length; i < len; ++i) { if (exp.test(strs[i])) { diff --git a/src/resources/marked.js b/src/resources/marked.js index 206cef9c..78688edf 100644 --- a/src/resources/marked.js +++ b/src/resources/marked.js @@ -71,6 +71,7 @@ var updateText = function(text) { renderPlantUML('language-puml'); renderGraphviz('language-dot'); addClassToCodeBlock(); + addCopyButtonToCodeBlock(); renderCodeBlockLineNumber(); // If you add new logics after handling MathJax, please pay attention to diff --git a/src/resources/showdown.js b/src/resources/showdown.js index 6e37b5ee..ea04e2a6 100644 --- a/src/resources/showdown.js +++ b/src/resources/showdown.js @@ -126,6 +126,7 @@ var updateText = function(text) { renderPlantUML('language-puml'); renderGraphviz('language-dot'); addClassToCodeBlock(); + addCopyButtonToCodeBlock(); renderCodeBlockLineNumber(); // If you add new logics after handling MathJax, please pay attention to diff --git a/src/utils/clipboard.js/README.md b/src/utils/clipboard.js/README.md new file mode 100644 index 00000000..61bfead3 --- /dev/null +++ b/src/utils/clipboard.js/README.md @@ -0,0 +1,2 @@ +# [clipboard.js](https://github.com/zenorocha/clipboard.js) +v2.0.4 diff --git a/src/utils/clipboard.js/clipboard.min.js b/src/utils/clipboard.js/clipboard.min.js new file mode 100644 index 00000000..02c549e3 --- /dev/null +++ b/src/utils/clipboard.js/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.4 + * https://zenorocha.github.io/clipboard.js + * + * Licensed MIT © Zeno Rocha + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return function(n){var o={};function r(t){if(o[t])return o[t].exports;var e=o[t]={i:t,l:!1,exports:{}};return n[t].call(e.exports,e,e.exports,r),e.l=!0,e.exports}return r.m=n,r.c=o,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=0)}([function(t,e,n){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=function(){function o(t,e){for(var n=0;n\n%1\n\n").arg(js); } + // Clipboard.js. + { + const QString clipboardjs(":/utils/clipboard.js/clipboard.min.js"); + QString js = VUtils::readFileFromDisk(clipboardjs); + extra += QString("\n").arg(js); + extra += "\n"; + } + if (!extra.isEmpty()) { templ.replace(HtmlHolder::c_extraHolder, extra); } diff --git a/src/vnote.qrc b/src/vnote.qrc index e4cc7774..d7f492af 100644 --- a/src/vnote.qrc +++ b/src/vnote.qrc @@ -287,5 +287,6 @@ resources/icons/table.svg utils/wavedrom/wavedrom.min.js utils/wavedrom/wavedrom-theme.js + utils/clipboard.js/clipboard.min.js