diff --git a/README.md b/README.md index 9d3ba7fa..c7da350e 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ | 4 | commander | 命令行环境 | 是 | | 5 | read_only | 只读模式 | 是 | | 6 | collapse_paragraph | 章节折叠 | 是 | -| 7 | copy_code | 一键复制代码 | 是 | +| 7 | fence_enhance | 一键复制代码,折叠代码 | 是 | | 8 | resize_table | 调整表格行高列宽 | 是 | | 9 | resize_image | 调整图片显示大小 | 是 | | 10 | go_top | 一键到文章顶部 | 是 | @@ -193,9 +193,13 @@ ctrl+鼠标滚轮滚动,修改图片大小。 -### copy_code:一键复制代码 +### fence_enhance:一键复制代码,折叠代码 -![copy_code](assets/copy_code.png) +![fence_enhance](assets/fence_enhance.png) + +> Fold、Copy 可选,如不需要,可以关闭任意一个。 + +> fence_enhance.js 易于扩展,你可以根据自己的需要添加功能,比如显示代码块编程语言。 diff --git a/assets/fence_enhance.png b/assets/fence_enhance.png new file mode 100644 index 00000000..7b67163c Binary files /dev/null and b/assets/fence_enhance.png differ diff --git a/plugin/copy_code.js b/plugin/fence_enhance.js similarity index 50% rename from plugin/copy_code.js rename to plugin/fence_enhance.js index ca92798c..d3a2bbde 100644 --- a/plugin/copy_code.js +++ b/plugin/fence_enhance.js @@ -1,6 +1,6 @@ (() => { /* 1. Typora是延迟加载页面的,文件内容都是通过延迟执行的js写入document的,具体代码在frame.js中$__System.registerDynamic函数中。 - 2. 糟糕的是md-fences在frame.js中是很晚生成的,并且有清空innerHTML操作的,你必须等到frame.js执行完毕后才能你的脚本,使用onload,DOMContentLoaded,statechange什么的都不行。 + 2. 糟糕的是md-fences在frame.js中是很晚生成的,并且有清空innerHTML操作的,你必须等到frame.js执行完毕后才能执行你的脚本,使用onload,DOMContentLoaded,statechange什么的都不行。 否则你插入的标签都会被清空,一切白费。原因很简单:frame.js执行的时机很晚,你可以认为是一个在网页全部加载完毕后执行的Ajax请求。 3. 这里给出清空md-fences的函数链条,打个断点就知道了: restoreEditStateFromData -> refresh -> refreshUnder -> refreshEditor -> addCodeBlock -> b.addClass("ty-contain-cm").html(""); @@ -14,6 +14,13 @@ const config = { // 启用脚本,若为false,以下配置全部失效 ENABLE: true, + // 启用复制代码功能 + ENABLE_COPY: true, + // 启用折叠代码功能 + ENABLE_FOLD: true, + // 折叠形式 + FOLD_OVERFLOW: "hidden", + LOOP_DETECT_INTERVAL: 20, CLICK_CHECK_INTERVAL: 300, } @@ -24,18 +31,24 @@ (() => { const css = ` - #write .md-fences .typora-copy-code { + #write .md-fences .fence-enhance { + display: inline-flex; position: absolute; top: .1em; right: .5em; - z-index: 99999; + z-index: 8; + } + #write .fence-enhance .typora-copy-code, .typora-fold-code { + opacity: 0.5; color: #4183C4; - opacity: 0.6; font-weight: bold; - border-bottom: 1px solid #4183C4; cursor: pointer; + border-bottom: 1px solid #4183C4; + } + #write .fence-enhance .typora-copy-code { + margin-left: 10px; } - #write .md-fences .typora-copy-code.copied { + #write .fence-enhance .typora-copy-code.copied, .typora-fold-code.folded { color: purple; border-color: purple; } @@ -46,63 +59,86 @@ document.getElementsByTagName("head")[0].appendChild(style); })() - const addCopyElement = (target) => { - let a = target.querySelector(".typora-copy-code"); - if (!a) { - a = document.createElement("a"); - a.setAttribute("class", "typora-copy-code"); - a.innerText = "Copy"; - target.appendChild(a); - } - } + const addEnhanceElement = fence => { + let enhance = fence.querySelector(".fence-enhance"); + if (!enhance) { + enhance = document.createElement("div"); + enhance.setAttribute("class", "fence-enhance"); + + if (config.ENABLE_FOLD) { + const foldButton = document.createElement("div"); + foldButton.classList.add("typora-fold-code"); + foldButton.innerText = "Fold"; + enhance.appendChild(foldButton); + } - const decorator = (original, after) => { - return function () { - const result = original.apply(this, arguments); - after.call(this, result, ...arguments); - return result; - }; - } + if (config.ENABLE_COPY) { + const copyButton = document.createElement("div"); + copyButton.classList.add("typora-copy-code"); + copyButton.innerText = "Copy"; + enhance.appendChild(copyButton); + } - const after = (result, ...args) => { - const cid = args[0]; - if (cid) { - const ele = document.querySelector(`#write .md-fences[cid=${cid}]`); - addCopyElement(ele); + fence.appendChild(enhance); } } const _timer = setInterval(() => { if (File && File.editor && File.editor.fences && File.editor.fences.addCodeBlock) { clearInterval(_timer); + + const decorator = (original, after) => { + return function () { + const result = original.apply(this, arguments); + after.call(this, result, ...arguments); + return result; + }; + } + + const after = (result, ...args) => { + const cid = args[0]; + if (cid) { + const ele = document.querySelector(`#write .md-fences[cid=${cid}]`); + addEnhanceElement(ele); + } + } File.editor.fences.addCodeBlock = decorator(File.editor.fences.addCodeBlock, after); } }, config.LOOP_DETECT_INTERVAL); - const badChars = [ - "%E2%80%8B", // ZERO WIDTH SPACE \u200b - "%C2%A0", // NO-BREAK SPACE \u00A0 - "%0A" // NO-BREAK SPACE \u0A - ]; - const replaceChars = ["", "%20", ""]; - - let lastClickTime = 0; document.getElementById("write").addEventListener("click", ev => { - const button = ev.target.closest(".typora-copy-code"); - if (!button) { + const copy = config.ENABLE_COPY && ev.target.closest(".typora-copy-code"); + const fold = config.ENABLE_FOLD && ev.target.closest(".typora-fold-code"); + if (!copy && !fold) { return } ev.preventDefault(); ev.stopPropagation(); + if (copy) { + copyCode(ev, copy); + } else { + foldCode(ev, fold); + } + }) + + let lastClickTime = 0; + const badChars = [ + "%E2%80%8B", // ZERO WIDTH SPACE \u200b + "%C2%A0", // NO-BREAK SPACE \u00A0 + "%0A" // NO-BREAK SPACE \u0A + ]; + const replaceChars = ["", "%20", ""]; + + const copyCode = (ev, copyButton) => { if (ev.timeStamp - lastClickTime < config.CLICK_CHECK_INTERVAL) { return } lastClickTime = ev.timeStamp; - const lines = button.closest(".md-fences").querySelectorAll(".CodeMirror-code .CodeMirror-line") - if (!lines) { + const lines = copyButton.closest(".md-fences").querySelectorAll(".CodeMirror-code .CodeMirror-line") + if (lines.length === 0) { return } @@ -123,13 +159,32 @@ const result = contentList.join("\n"); navigator.clipboard.writeText(result); - button.classList.add("copied"); - button.innerText = "Copied"; + copyButton.classList.add("copied"); + copyButton.innerText = "Copied"; setTimeout(() => { - button.classList.remove("copied"); - button.innerText = "Copy"; + copyButton.classList.remove("copied"); + copyButton.innerText = "Copy"; }, 1000) - }) + } + + const foldCode = (ev, foldButton) => { + const scroll = foldButton.closest(".md-fences").querySelector(".CodeMirror-scroll"); + if (!scroll) { + return + } + document.activeElement.blur(); + if (scroll.style.height && scroll.style.overflowY) { + scroll.style.height = ""; + scroll.style.overflowY = ""; + foldButton.classList.remove("folded"); + foldButton.innerText = "Fold"; + } else { + scroll.style.height = window.getComputedStyle(foldButton).lineHeight; + scroll.style.overflowY = config.FOLD_OVERFLOW; + foldButton.classList.add("folded"); + foldButton.innerText = "Folded"; + } + } - console.log("copy_code.js had been injected"); + console.log("fence_enhance.js had been injected"); })() \ No newline at end of file diff --git a/plugin/index.js b/plugin/index.js index ec8ec675..61c75c5b 100644 --- a/plugin/index.js +++ b/plugin/index.js @@ -27,7 +27,7 @@ window.onload = () => { _require('truncate_text', './plugin/truncate_text.js'); _require('resize_image', './plugin/resize_image.js'); _require('commander', './plugin/commander.js'); - _require('copy_code', './plugin/copy_code.js'); + _require('fence_enhance', './plugin/fence_enhance.js'); _require('go_top', './plugin/go_top.js'); _require('file_counter', './plugin/file_counter.js'); _require('collapse_paragraph', './plugin/collapse_paragraph.js');