diff --git a/.eslintrc.js b/.eslintrc.js index def4470..f94a64a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,28 +1,27 @@ module.exports = { root: true, env: { - node: true + node: true, }, - 'extends': [ - 'plugin:vue/essential', - '@vue/standard' - ], + extends: ["plugin:vue/essential", "@vue/standard"], rules: { - 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', - 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', - 'camelcase': 'off', - 'eqeqeq': 'off' + "no-console": process.env.NODE_ENV === "production" ? "error" : "off", + "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off", + camelcase: "off", + eqeqeq: "off", }, parserOptions: { - parser: 'babel-eslint' + parser: "babel-eslint", }, - overrides: [{ - files: [ - '**/__tests__/*.{j,t}s?(x)', - '**/tests/unit/**/*.spec.{j,t}s?(x)' - ], - env: { - jest: true - } - }] -} + overrides: [ + { + files: [ + "**/__tests__/*.{j,t}s?(x)", + "**/tests/unit/**/*.spec.{j,t}s?(x)", + ], + env: { + jest: true, + }, + }, + ], +}; diff --git a/README.md b/README.md index b572a59..189670b 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,9 @@
- [![doocs-md](./public/assets/images/logo-2.png)](https://github.com/doocs/md) +[![doocs-md](./public/assets/images/logo-2.png)](https://github.com/doocs/md)
-

微信 Markdown 编辑器

@@ -13,14 +12,12 @@
- ## 项目介绍 > 本项目基于 [wechat-format](https://github.com/lyricat/wechat-format) 进行二次开发,感谢 [lyricat](https://github.com/lyricat) 的创意和贡献! Markdown 文档自动即时渲染为微信图文,让你不再为微信文章排版而发愁!只要你会基本的 Markdown 语法,就能做出一篇样式简洁而又美观大方的微信图文。 - ## 在线编辑器地址 - Gitee Pages:https://doocs.gitee.io/md @@ -28,7 +25,6 @@ Markdown 文档自动即时渲染为微信图文,让你不再为微信文章 注:推荐使用 Chrome 浏览器,效果最佳。另外,对于国内(中国)的朋友,访问 [Gitee Pages](https://doocs.gitee.io/md) 速度会相对快一些。 - ## 为何二次开发 现有的开源微信 Markdown 编辑器,样式繁杂,也不符合我个人的审美需求。在我使用它们进行文章排版的时候,经常还要自己做一些改动,费时费力,因此动手做了二次开发。 @@ -47,13 +43,12 @@ Markdown 文档自动即时渲染为微信图文,让你不再为微信文章 ## 目前支持哪些图床 -| # | 图床 | 使用时是否需要配置 | 备注 | -|---|---|---|---| -| 1 | 默认图床 | 否 | - | -| 2 | GitHub 图床 | 配置 `Repo`、`Token` 参数 | [如何获取 GitHub token?](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) | -| 3 | 阿里云 OSS | 配置 `AccessKey ID`、`AccessKey Secret`、`Bucket`、`Region` 等参数 | [如何使用阿里云 OSS?](https://help.aliyun.com/document_detail/31883.html) | -| 4 | 腾讯云 COS | 配置 `SecretId`、`SecretKey`、`Bucket`、`Region` 等参数 | [如何使用腾讯云 COS?](https://cloud.tencent.com/document/product/436/38484) | - +| # | 图床 | 使用时是否需要配置 | 备注 | +| --- | ----------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------- | +| 1 | 默认图床 | 否 | - | +| 2 | GitHub 图床 | 配置 `Repo`、`Token` 参数 | [如何获取 GitHub token?](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) | +| 3 | 阿里云 OSS | 配置 `AccessKey ID`、`AccessKey Secret`、`Bucket`、`Region` 等参数 | [如何使用阿里云 OSS?](https://help.aliyun.com/document_detail/31883.html) | +| 4 | 腾讯云 COS | 配置 `SecretId`、`SecretKey`、`Bucket`、`Region` 等参数 | [如何使用腾讯云 COS?](https://cloud.tencent.com/document/product/436/38484) | ![select-and-change-color-theme](./public/assets/images/select-and-change-color-theme.gif) @@ -61,8 +56,7 @@ Markdown 文档自动即时渲染为微信图文,让你不再为微信文章 ![custom](./public/assets/images/custom.gif) -![doocs-md-upload-image](./public/assets/images/doocs-md-upload-image.gif) - +![doocs-md-upload-image](./public/assets/images/doocs-md-upload-image.gif) ## 谁在使用 @@ -165,28 +159,25 @@ Markdown 文档自动即时渲染为微信图文,让你不再为微信文章 注:如果你使用了本 Markdown 编辑器进行文章排版,并且希望在本项目 README 中展示你的公众号,请到 [#5](https://github.com/doocs/md/issues/5) 留言。 - ## 项目许可证 -[本项目没有任何限制,Just Do What The F*ck You Want。](LICENSE) +[本项目没有任何限制,Just Do What The F\*ck You Want。](LICENSE) --- - ## Doocs 社区优质项目 Doocs 技术社区,致力于打造一个内容完整、持续成长的互联网开发者学习生态圈!以下是 Doocs 旗下的一些优秀项目,欢迎各位开发者朋友持续保持关注。 -| # | 项目 | 描述 | 热度 | -|---|---|---|---| -| 1 | [advanced-java](https://github.com/doocs/advanced-java) | 互联网 Java 工程师进阶知识完全扫盲:涵盖高并发、分布式、高可用、微服务、海量数据处理等领域知识。 | ![](https://badgen.net/github/stars/doocs/advanced-java)
![](https://badgen.net/github/forks/doocs/advanced-java) | -| 2 | [leetcode](https://github.com/doocs/leetcode) | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解。 | ![](https://badgen.net/github/stars/doocs/leetcode)
![](https://badgen.net/github/forks/doocs/leetcode) | -| 3 | [source-code-hunter](https://github.com/doocs/source-code-hunter) | 互联网常用组件框架源码分析。 | ![](https://badgen.net/github/stars/doocs/source-code-hunter)
![](https://badgen.net/github/forks/doocs/source-code-hunter) | -| 4 | [jvm](https://github.com/doocs/jvm) | Java 虚拟机底层原理知识总结。 | ![](https://badgen.net/github/stars/doocs/jvm)
![](https://badgen.net/github/forks/doocs/jvm) | -| 5 | [coding-interview](https://github.com/doocs/coding-interview) | 代码面试题集,包括《剑指 Offer》、《编程之美》等。 | ![](https://badgen.net/github/stars/doocs/coding-interview)
![](https://badgen.net/github/forks/doocs/coding-interview) | -| 6 | [md](https://github.com/doocs/md) | 一款高度简洁的微信 Markdown 编辑器。 | ![](https://badgen.net/github/stars/doocs/md)
![](https://badgen.net/github/forks/doocs/md) | -| 7 | [technical-books](https://github.com/doocs/technical-books) | 值得一看的技术书籍列表。 | ![](https://badgen.net/github/stars/doocs/technical-books)
![](https://badgen.net/github/forks/doocs/technical-books) | - +| # | 项目 | 描述 | 热度 | +| --- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | +| 1 | [advanced-java](https://github.com/doocs/advanced-java) | 互联网 Java 工程师进阶知识完全扫盲:涵盖高并发、分布式、高可用、微服务、海量数据处理等领域知识。 | ![](https://badgen.net/github/stars/doocs/advanced-java)
![](https://badgen.net/github/forks/doocs/advanced-java) | +| 2 | [leetcode](https://github.com/doocs/leetcode) | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解。 | ![](https://badgen.net/github/stars/doocs/leetcode)
![](https://badgen.net/github/forks/doocs/leetcode) | +| 3 | [source-code-hunter](https://github.com/doocs/source-code-hunter) | 互联网常用组件框架源码分析。 | ![](https://badgen.net/github/stars/doocs/source-code-hunter)
![](https://badgen.net/github/forks/doocs/source-code-hunter) | +| 4 | [jvm](https://github.com/doocs/jvm) | Java 虚拟机底层原理知识总结。 | ![](https://badgen.net/github/stars/doocs/jvm)
![](https://badgen.net/github/forks/doocs/jvm) | +| 5 | [coding-interview](https://github.com/doocs/coding-interview) | 代码面试题集,包括《剑指 Offer》、《编程之美》等。 | ![](https://badgen.net/github/stars/doocs/coding-interview)
![](https://badgen.net/github/forks/doocs/coding-interview) | +| 6 | [md](https://github.com/doocs/md) | 一款高度简洁的微信 Markdown 编辑器。 | ![](https://badgen.net/github/stars/doocs/md)
![](https://badgen.net/github/forks/doocs/md) | +| 7 | [technical-books](https://github.com/doocs/technical-books) | 值得一看的技术书籍列表。 | ![](https://badgen.net/github/stars/doocs/technical-books)
![](https://badgen.net/github/forks/doocs/technical-books) | ## 贡献者 diff --git a/babel.config.js b/babel.config.js index c94e729..6431b56 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,5 +1,3 @@ module.exports = { - presets: [ - '@vue/cli-plugin-babel/preset' - ] -} + presets: ["@vue/cli-plugin-babel/preset"], +}; diff --git a/jest.config.js b/jest.config.js index 15e1b1e..14065ad 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,3 +1,3 @@ module.exports = { - preset: '@vue/cli-plugin-unit-jest' -} + preset: "@vue/cli-plugin-unit-jest", +}; diff --git a/public/index.html b/public/index.html index 254f774..2aa2d96 100644 --- a/public/index.html +++ b/public/index.html @@ -1,23 +1,33 @@ - - - + + - - - + + + 微信 Markdown 编辑器 - - + + - + - -
-
- - - \ No newline at end of file + +
+ + diff --git a/src/api/fetch.js b/src/api/fetch.js index d401d9e..dc16ee2 100644 --- a/src/api/fetch.js +++ b/src/api/fetch.js @@ -1,26 +1,30 @@ -import axios from 'axios'; +import axios from "axios"; // 创建axios实例 const service = axios.create({ - baseURL: '', - timeout: 10 * 1000 // 请求超时时间 + baseURL: "", + timeout: 10 * 1000, // 请求超时时间 }); service.interceptors.request.use( - config => { + (config) => { if (/^(post)|(put)|(delete)$/i.test(config.method)) { if (config.data && config.data.upload) { - config.headers['Content-Type'] = 'multipart/form-data'; + config.headers["Content-Type"] = "multipart/form-data"; } } return config; - }, error => { + }, + (error) => { Promise.reject(error); } ); -service.interceptors.response.use(res => { - return res.data ? res.data : Promise.reject(res); -}, error => Promise.reject(error)); +service.interceptors.response.use( + (res) => { + return res.data ? res.data : Promise.reject(res); + }, + (error) => Promise.reject(error) +); -export default service; \ No newline at end of file +export default service; diff --git a/src/api/file.js b/src/api/file.js index ea60d07..2fcf88f 100644 --- a/src/api/file.js +++ b/src/api/file.js @@ -1,37 +1,35 @@ -import fetch from './fetch'; -import OSS from 'ali-oss'; -import COS from 'cos-js-sdk-v5'; -import Buffer from 'buffer-from'; -import { - v4 as uuidv4 -} from 'uuid'; -import { resolveConfigFile } from 'prettier'; +import fetch from "./fetch"; +import OSS from "ali-oss"; +import COS from "cos-js-sdk-v5"; +import Buffer from "buffer-from"; +import { v4 as uuidv4 } from "uuid"; +import { resolveConfigFile } from "prettier"; const defaultConfig = { - username: 'filess', - repo: 'images', - branch: 'master', + username: "filess", + repo: "images", + branch: "master", accessToken: [ - '7715d7ca67b5d3837cfdoocsmde8c38421815aa423510af', - 'c411415bf95dbe39625doocsmd5047ba9b7a2a6c9642abe', - '2821cd8819fa345c053doocsmdca86ac653f8bc20db1f1b', - '445f0dae46ef1f2a4d6doocsmdc797301e94797b4750a4c', - 'cc1d0c1426d0fd0902bdoocsmdd2d7184b14da61b86ec46', - 'b67e9d15cb6f910492fdoocsmdac6b44d379c953bb19eff', - '618c4dc2244ccbbc088doocsmd125d17fd31b7d06a50cf3', - 'a4b581732e1c1507458doocsmdc5b223b27dae5e2e16a55' - ] -} + "7715d7ca67b5d3837cfdoocsmde8c38421815aa423510af", + "c411415bf95dbe39625doocsmd5047ba9b7a2a6c9642abe", + "2821cd8819fa345c053doocsmdca86ac653f8bc20db1f1b", + "445f0dae46ef1f2a4d6doocsmdc797301e94797b4750a4c", + "cc1d0c1426d0fd0902bdoocsmdd2d7184b14da61b86ec46", + "b67e9d15cb6f910492fdoocsmdac6b44d379c953bb19eff", + "618c4dc2244ccbbc088doocsmd125d17fd31b7d06a50cf3", + "a4b581732e1c1507458doocsmdc5b223b27dae5e2e16a55", + ], +}; function fileUpload(content, file) { - const imgHost = localStorage.getItem('imgHost'); - !imgHost && localStorage.setItem('imgHost', 'default'); + const imgHost = localStorage.getItem("imgHost"); + !imgHost && localStorage.setItem("imgHost", "default"); switch (imgHost) { - case 'aliOSS': + case "aliOSS": return aliOSSFileUpload(content, file.name); - case 'txCOS': + case "txCOS": return txCOSFileUpload(file); - case 'github': + case "github": default: return ghFileUpload(content, file.name); } @@ -39,96 +37,136 @@ function fileUpload(content, file) { function getGitHubCommonConfig(username, repo, branch, token) { const date = new Date(); - const dir = date.getFullYear() + '/' + (date.getMonth() + 1).toString().padStart(2, '0') + '/' + date.getDate().toString().padStart(2, '0'); + const dir = + date.getFullYear() + + "/" + + (date.getMonth() + 1).toString().padStart(2, "0") + + "/" + + date.getDate().toString().padStart(2, "0"); return { - method: 'put', + method: "put", headers: { - 'Authorization': 'token ' + token + Authorization: "token " + token, }, branch: branch, - url: `https://api.github.com/repos/${username}/${repo}/contents/${dir}/` + url: `https://api.github.com/repos/${username}/${repo}/contents/${dir}/`, }; } function getDefaultConfig() { - const token = defaultConfig.accessToken[Math.floor(Math.random() * defaultConfig.accessToken.length)].replace('doocsmd', ''); - return getGitHubCommonConfig(defaultConfig.username, defaultConfig.repo, defaultConfig.branch, token); + const token = defaultConfig.accessToken[ + Math.floor(Math.random() * defaultConfig.accessToken.length) + ].replace("doocsmd", ""); + return getGitHubCommonConfig( + defaultConfig.username, + defaultConfig.repo, + defaultConfig.branch, + token + ); } function getGitHubConfig() { const githubConfig = JSON.parse(localStorage.getItem("githubConfig")); - const repoUrl = githubConfig.repo.replace("https://github.com/", "").replace("http://github.com/", "").replace("github.com/", "").split("/"); + const repoUrl = githubConfig.repo + .replace("https://github.com/", "") + .replace("http://github.com/", "") + .replace("github.com/", "") + .split("/"); const username = repoUrl[0]; const repo = repoUrl[1]; - return getGitHubCommonConfig(username, repo, githubConfig.branch, githubConfig.accessToken); + return getGitHubCommonConfig( + username, + repo, + githubConfig.branch, + githubConfig.accessToken + ); } async function ghFileUpload(content, filename) { - const isDefault = localStorage.getItem('imgHost') !== 'github'; + const isDefault = localStorage.getItem("imgHost") !== "github"; const config = isDefault ? getDefaultConfig() : getGitHubConfig(); - const dateFilename = new Date().getTime() + '-' + uuidv4() + '.' + filename.split('.')[1]; + const dateFilename = + new Date().getTime() + "-" + uuidv4() + "." + filename.split(".")[1]; const res = await fetch({ url: config.url + dateFilename, method: config.method, headers: config.headers, data: { - branch: config.branch || 'master', + branch: config.branch || "master", message: `Upload by ${window.location.href}`, - content: content - } + content: content, + }, }); - const githubResourceUrl = 'raw.githubusercontent.com/filess/images/master/'; - const cdnResourceUrl = 'cdn.jsdelivr.net/gh/filess/images/'; - return isDefault ? res.content.download_url.replace(githubResourceUrl, cdnResourceUrl) : res.content.download_url; - + const githubResourceUrl = "raw.githubusercontent.com/filess/images/master/"; + const cdnResourceUrl = "cdn.jsdelivr.net/gh/filess/images/"; + return isDefault + ? res.content.download_url.replace(githubResourceUrl, cdnResourceUrl) + : res.content.download_url; } async function aliOSSFileUpload(content, filename) { - const dateFilename = new Date().getTime() + '-' + uuidv4() + '.' + filename.split('.')[1]; - const aliOSSConfig = JSON.parse(localStorage.getItem('aliOSSConfig')); - const buffer = Buffer(content, 'base64'); + const dateFilename = + new Date().getTime() + "-" + uuidv4() + "." + filename.split(".")[1]; + const aliOSSConfig = JSON.parse(localStorage.getItem("aliOSSConfig")); + const buffer = Buffer(content, "base64"); try { - const dir = aliOSSConfig.path + '/' + dateFilename; + const dir = aliOSSConfig.path + "/" + dateFilename; const client = new OSS({ region: aliOSSConfig.region, bucket: aliOSSConfig.bucket, accessKeyId: aliOSSConfig.accessKeyId, - accessKeySecret: aliOSSConfig.accessKeySecret + accessKeySecret: aliOSSConfig.accessKeySecret, }); const res = await client.put(dir, buffer); - return aliOSSConfig.cdnHost == '' ? res.url : aliOSSConfig.cdnHost + '/' + (aliOSSConfig.path == '' ? dateFilename : dir); + return aliOSSConfig.cdnHost == "" + ? res.url + : aliOSSConfig.cdnHost + + "/" + + (aliOSSConfig.path == "" ? dateFilename : dir); } catch (e) { return Promise.reject(e); } } async function txCOSFileUpload(file) { - const dateFilename = new Date().getTime() + '-' + uuidv4() + '.' + file.name.split('.')[1]; - const txCOSConfig = JSON.parse(localStorage.getItem('txCOSConfig')); + const dateFilename = + new Date().getTime() + "-" + uuidv4() + "." + file.name.split(".")[1]; + const txCOSConfig = JSON.parse(localStorage.getItem("txCOSConfig")); const cos = new COS({ SecretId: txCOSConfig.secretId, - SecretKey: txCOSConfig.secretKey + SecretKey: txCOSConfig.secretKey, }); return new Promise((resolve, reject) => { - cos.putObject({ - Bucket: txCOSConfig.bucket, - Region: txCOSConfig.region, - Key: txCOSConfig.path + '/' + dateFilename, - Body: file - }, function (err, data) { - if (err) { - reject(err); - } else if (txCOSConfig.cdnHost) { - // if cdnHost exists - resolve(txCOSConfig.path != '' ? txCOSConfig.cdnHost + '/' + txCOSConfig.path + '/' + dateFilename : txCOSConfig.cdnHost + '/' + dateFilename); - } else { - // if cdnHost not exists - reject(data.Location); + cos.putObject( + { + Bucket: txCOSConfig.bucket, + Region: txCOSConfig.region, + Key: txCOSConfig.path + "/" + dateFilename, + Body: file, + }, + function (err, data) { + if (err) { + reject(err); + } else if (txCOSConfig.cdnHost) { + // if cdnHost exists + resolve( + txCOSConfig.path != "" + ? txCOSConfig.cdnHost + + "/" + + txCOSConfig.path + + "/" + + dateFilename + : txCOSConfig.cdnHost + "/" + dateFilename + ); + } else { + // if cdnHost not exists + reject(data.Location); + } } - }); - }) + ); + }); } export default { - fileUpload + fileUpload, }; diff --git a/src/assets/scripts/closebrackets.js b/src/assets/scripts/closebrackets.js index ce43d71..1434e64 100644 --- a/src/assets/scripts/closebrackets.js +++ b/src/assets/scripts/closebrackets.js @@ -6,12 +6,16 @@ import CodeMirror from "codemirror/lib/codemirror"; pairs: "()[]{}''\"\"", closeBefore: ")]}'\":;>", triples: "", - explode: "[]{}" + explode: "[]{}", }; var Pos = CodeMirror.Pos; - CodeMirror.defineOption("autoCloseBrackets", false, function (cm, val, old) { + CodeMirror.defineOption("autoCloseBrackets", false, function ( + cm, + val, + old + ) { if (old && old != CodeMirror.Init) { cm.removeKeyMap(keyMap); cm.state.closeBrackets = null; @@ -31,7 +35,7 @@ import CodeMirror from "codemirror/lib/codemirror"; var keyMap = { Backspace: handleBackspace, - Enter: handleEnter + Enter: handleEnter, }; function ensureBound(chars) { @@ -65,7 +69,8 @@ import CodeMirror from "codemirror/lib/codemirror"; for (var i = 0; i < ranges.length; i++) { if (!ranges[i].empty()) return CodeMirror.Pass; var around = charsAround(cm, ranges[i].head); - if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass; + if (!around || pairs.indexOf(around) % 2 != 0) + return CodeMirror.Pass; } for (var i = ranges.length - 1; i >= 0; i--) { var cur = ranges[i].head; @@ -87,7 +92,8 @@ import CodeMirror from "codemirror/lib/codemirror"; for (var i = 0; i < ranges.length; i++) { if (!ranges[i].empty()) return CodeMirror.Pass; var around = charsAround(cm, ranges[i].head); - if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass; + if (!around || explode.indexOf(around) % 2 != 0) + return CodeMirror.Pass; } cm.operation(function () { var linesep = cm.lineSeparator() || "\n"; @@ -105,8 +111,11 @@ import CodeMirror from "codemirror/lib/codemirror"; function contractSelection(sel) { var inverted = CodeMirror.cmpPos(sel.anchor, sel.head) > 0; return { - anchor: new Pos(sel.anchor.line, sel.anchor.ch + (inverted ? -1 : 1)), - head: new Pos(sel.head.line, sel.head.ch + (inverted ? 1 : -1)) + anchor: new Pos( + sel.anchor.line, + sel.anchor.ch + (inverted ? -1 : 1) + ), + head: new Pos(sel.head.line, sel.head.ch + (inverted ? 1 : -1)), }; } @@ -150,13 +159,17 @@ import CodeMirror from "codemirror/lib/codemirror"; ) { if ( cur.ch > 2 && - /\bstring/.test(cm.getTokenTypeAt(Pos(cur.line, cur.ch - 2))) + /\bstring/.test( + cm.getTokenTypeAt(Pos(cur.line, cur.ch - 2)) + ) ) return CodeMirror.Pass; curType = "addFour"; } else if (identical) { var prev = - cur.ch == 0 ? " " : cm.getRange(Pos(cur.line, cur.ch - 1), cur); + cur.ch == 0 + ? " " + : cm.getRange(Pos(cur.line, cur.ch - 1), cur); if ( !CodeMirror.isWordChar(next) && prev != ch && @@ -166,7 +179,9 @@ import CodeMirror from "codemirror/lib/codemirror"; else return CodeMirror.Pass; } else if ( opening && - (next.length === 0 || /\s/.test(next) || closeBefore.indexOf(next) > -1) + (next.length === 0 || + /\s/.test(next) || + closeBefore.indexOf(next) > -1) ) { curType = "both"; } else { @@ -185,7 +200,8 @@ import CodeMirror from "codemirror/lib/codemirror"; for (var i = 0; i < 3; i++) cm.execCommand("goCharRight"); } else if (type == "surround") { var sels = cm.getSelections(); - for (var i = 0; i < sels.length; i++) sels[i] = left + sels[i] + right; + for (var i = 0; i < sels.length; i++) + sels[i] = left + sels[i] + right; cm.replaceSelections(sels, "around"); sels = cm.listSelections().slice(); for (var i = 0; i < sels.length; i++) @@ -203,7 +219,10 @@ import CodeMirror from "codemirror/lib/codemirror"; } function charsAround(cm, pos) { - var str = cm.getRange(Pos(pos.line, pos.ch - 1), Pos(pos.line, pos.ch + 1)); + var str = cm.getRange( + Pos(pos.line, pos.ch - 1), + Pos(pos.line, pos.ch + 1) + ); return str.length == 2 ? str : null; } diff --git a/src/assets/scripts/config.js b/src/assets/scripts/config.js index 414f95d..a0877b3 100644 --- a/src/assets/scripts/config.js +++ b/src/assets/scripts/config.js @@ -1,69 +1,74 @@ export default { - builtinFonts: [{ - label: '无衬线', - value: '-apple-system-font,BlinkMacSystemFont, Helvetica Neue, PingFang SC, Hiragino Sans GB , Microsoft YaHei UI , Microsoft YaHei ,Arial,sans-serif' + builtinFonts: [ + { + label: "无衬线", + value: + "-apple-system-font,BlinkMacSystemFont, Helvetica Neue, PingFang SC, Hiragino Sans GB , Microsoft YaHei UI , Microsoft YaHei ,Arial,sans-serif", }, { - label: '衬线', - value: "Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif" - } + label: "衬线", + value: + "Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif", + }, ], - sizeOption: [{ - label: '12px', - value: '12px', - desc: '更小' + sizeOption: [ + { + label: "12px", + value: "12px", + desc: "更小", }, { - label: '13px', - value: '13px', - desc: '稍小' + label: "13px", + value: "13px", + desc: "稍小", }, { - label: '14px', - value: '14px', - desc: '推荐' + label: "14px", + value: "14px", + desc: "推荐", }, { - label: '15px', - value: '15px', - desc: '稍大' + label: "15px", + value: "15px", + desc: "稍大", }, { - label: '16px', - value: '16px', - desc: '更大' - } + label: "16px", + value: "16px", + desc: "更大", + }, ], - colorOption: [{ - label: '经典蓝', - value: 'rgba(15, 76, 129, 1)', - desc: '最新流行' + colorOption: [ + { + label: "经典蓝", + value: "rgba(15, 76, 129, 1)", + desc: "最新流行", }, { - label: '翡翠绿', - value: 'rgba(0, 152, 116, 1)', - desc: '优雅清新' + label: "翡翠绿", + value: "rgba(0, 152, 116, 1)", + desc: "优雅清新", }, { - label: '活力橘', - value: 'rgba(250, 81, 81, 1)', - desc: '热情活泼' - } + label: "活力橘", + value: "rgba(250, 81, 81, 1)", + desc: "热情活泼", + }, ], codeThemeOption: [ { - label: '微信', - value: 'wechat', - desc: '默认样式' + label: "微信", + value: "wechat", + desc: "默认样式", }, { - label: 'GitHub', - value: 'github', - desc: '精简风格' - } + label: "GitHub", + value: "github", + desc: "精简风格", + }, ], form: { rows: 1, - cols: 1 - } + cols: 1, + }, }; diff --git a/src/assets/scripts/converter.js b/src/assets/scripts/converter.js index c61105a..00a3a4e 100644 --- a/src/assets/scripts/converter.js +++ b/src/assets/scripts/converter.js @@ -1,7 +1,7 @@ -import juice from 'juice' +import juice from "juice"; export function solveWeChatImage() { - const clipboardDiv = document.getElementById('output'); + const clipboardDiv = document.getElementById("output"); const images = clipboardDiv.getElementsByTagName("img"); for (let i = 0; i < images.length; i++) { const image = images[i]; @@ -15,13 +15,11 @@ export function solveWeChatImage() { } export function solveHtml() { const element = document.getElementById("output-wrapper"); - let html = element.innerHTML + let html = element.innerHTML; let res = ""; - res = juice.inlineContent( - html, { - inlinePseudoElements: true, - preserveImportant: true - } - ); + res = juice.inlineContent(html, { + inlinePseudoElements: true, + preserveImportant: true, + }); return res; } diff --git a/src/assets/scripts/default-content.js b/src/assets/scripts/default-content.js index e15ae44..b06adf3 100644 --- a/src/assets/scripts/default-content.js +++ b/src/assets/scripts/default-content.js @@ -1,5 +1,4 @@ -const DEFAULT_CONTENT = -`# 示例文章:Google 搜索的即时自动补全功能究竟是如何“工作”的? +const DEFAULT_CONTENT = `# 示例文章:Google 搜索的即时自动补全功能究竟是如何“工作”的? > Google 搜索**自动补全功能**的强大,相信不少朋友都能感受到,它帮助我们更快地“补全”我们所要输入的搜索关键字。那么,它怎么知道我们要输入什么内容?它又是如何工作的?在这篇文章里,我们一起来看看。 ## 使用自动补全 @@ -96,5 +95,5 @@ Google 拥有专门设计的系统,可以自动捕获不适当的预测结果 -` -export default DEFAULT_CONTENT +`; +export default DEFAULT_CONTENT; diff --git a/src/assets/scripts/format.js b/src/assets/scripts/format.js index a6756b7..396ce7a 100644 --- a/src/assets/scripts/format.js +++ b/src/assets/scripts/format.js @@ -1,96 +1,120 @@ import CodeMirror from "codemirror/lib/codemirror"; (function () { - CodeMirror.extendMode('css', { - commentStart: '/*', - commentEnd: '*/', + CodeMirror.extendMode("css", { + commentStart: "/*", + commentEnd: "*/", newlineAfterToken: function (type, content) { - return /^[;{}]$/.test(content) - } - }) + return /^[;{}]$/.test(content); + }, + }); // Comment/uncomment the specified range - CodeMirror.defineExtension('commentRange', function (isComment, from, to) { + CodeMirror.defineExtension("commentRange", function (isComment, from, to) { var cm = this; - var curMode = CodeMirror.innerMode(cm.getMode(), cm.getTokenAt(from).state).mode + var curMode = CodeMirror.innerMode( + cm.getMode(), + cm.getTokenAt(from).state + ).mode; cm.operation(function () { - if (isComment) { // Comment range - cm.replaceRange(curMode.commentEnd, to) - cm.replaceRange(curMode.commentStart, from) - if (from.line == to.line && from.ch == to.ch) // An empty comment inserted - put cursor inside - { - cm.setCursor(from.line, from.ch + curMode.commentStart.length) + if (isComment) { + // Comment range + cm.replaceRange(curMode.commentEnd, to); + cm.replaceRange(curMode.commentStart, from); + if (from.line == to.line && from.ch == to.ch) { + // An empty comment inserted - put cursor inside + cm.setCursor( + from.line, + from.ch + curMode.commentStart.length + ); } - } else { // Uncomment range - var selText = cm.getRange(from, to) - var startIndex = selText.indexOf(curMode.commentStart) - var endIndex = selText.lastIndexOf(curMode.commentEnd) + } else { + // Uncomment range + var selText = cm.getRange(from, to); + var startIndex = selText.indexOf(curMode.commentStart); + var endIndex = selText.lastIndexOf(curMode.commentEnd); if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) { // Take string till comment start - selText = selText.substr(0, startIndex) + + selText = + selText.substr(0, startIndex) + // From comment start till comment end - selText.substring(startIndex + curMode.commentStart.length, endIndex) + + selText.substring( + startIndex + curMode.commentStart.length, + endIndex + ) + // From comment end till string end - selText.substr(endIndex + curMode.commentEnd.length) + selText.substr(endIndex + curMode.commentEnd.length); } - cm.replaceRange(selText, from, to) + cm.replaceRange(selText, from, to); } - }) - }) + }); + }); // Applies automatic mode-aware indentation to the specified range - CodeMirror.defineExtension('autoIndentRange', function (from, to) { - var cmInstance = this + CodeMirror.defineExtension("autoIndentRange", function (from, to) { + var cmInstance = this; this.operation(function () { for (var i = from.line; i <= to.line; i++) { - cmInstance.indentLine(i, 'smart') + cmInstance.indentLine(i, "smart"); } - }) - }) + }); + }); // Applies automatic formatting to the specified range - CodeMirror.defineExtension('autoFormatRange', function (from, to) { - var cm = this + CodeMirror.defineExtension("autoFormatRange", function (from, to) { + var cm = this; var outer = cm.getMode(); - var text = cm.getRange(from, to).split('\n') - var state = CodeMirror.copyState(outer, cm.getTokenAt(from).state) - var tabSize = cm.getOption('tabSize') + var text = cm.getRange(from, to).split("\n"); + var state = CodeMirror.copyState(outer, cm.getTokenAt(from).state); + var tabSize = cm.getOption("tabSize"); - var out = ''; + var out = ""; var lines = 0; - var atSol = from.ch == 0 + var atSol = from.ch == 0; function newline() { - out += '\n' - atSol = true - ++lines + out += "\n"; + atSol = true; + ++lines; } for (var i = 0; i < text.length; ++i) { - var stream = new CodeMirror.StringStream(text[i], tabSize) + var stream = new CodeMirror.StringStream(text[i], tabSize); while (!stream.eol()) { - var inner = CodeMirror.innerMode(outer, state) + var inner = CodeMirror.innerMode(outer, state); var style = outer.token(stream, state); - var cur = stream.current() - stream.start = stream.pos + var cur = stream.current(); + stream.start = stream.pos; if (!atSol || /\S/.test(cur)) { - out += cur - atSol = false + out += cur; + atSol = false; } - if (!atSol && inner.mode.newlineAfterToken && - inner.mode.newlineAfterToken(style, cur, stream.string.slice(stream.pos) || text[i + 1] || '', inner.state)) { - newline() + if ( + !atSol && + inner.mode.newlineAfterToken && + inner.mode.newlineAfterToken( + style, + cur, + stream.string.slice(stream.pos) || text[i + 1] || "", + inner.state + ) + ) { + newline(); } } - if (!stream.pos && outer.blankLine) outer.blankLine(state) - if (!atSol) newline() + if (!stream.pos && outer.blankLine) outer.blankLine(state); + if (!atSol) newline(); } cm.operation(function () { - cm.replaceRange(out, from, to) - for (var cur = from.line + 1, end = from.line + lines; cur <= end; ++cur) { - cm.indentLine(cur, 'smart') + cm.replaceRange(out, from, to); + for ( + var cur = from.line + 1, end = from.line + lines; + cur <= end; + ++cur + ) { + cm.indentLine(cur, "smart"); } - cm.setSelection(from, cm.getCursor(false)) - }) - }) -})() + cm.setSelection(from, cm.getCursor(false)); + }); + }); +})(); diff --git a/src/assets/scripts/renderers/wx-renderer.js b/src/assets/scripts/renderers/wx-renderer.js index ff4805d..f489d59 100644 --- a/src/assets/scripts/renderers/wx-renderer.js +++ b/src/assets/scripts/renderers/wx-renderer.js @@ -1,66 +1,69 @@ -import marked from 'marked'; +import marked from "marked"; const WxRenderer = function (opts) { - this.opts = opts - let ENV_STRETCH_IMAGE = true + this.opts = opts; + let ENV_STRETCH_IMAGE = true; - let footnotes = [] - let footnoteIndex = 0 - let styleMapping = null + let footnotes = []; + let footnoteIndex = 0; + let styleMapping = null; - const CODE_FONT_FAMILY = 'Menlo, Operator Mono, Consolas, Monaco, monospace' + const CODE_FONT_FAMILY = + "Menlo, Operator Mono, Consolas, Monaco, monospace"; - let merge = (base, extend) => Object.assign({}, base, extend) + let merge = (base, extend) => Object.assign({}, base, extend); - this.buildTheme = themeTpl => { - let mapping = {} + this.buildTheme = (themeTpl) => { + let mapping = {}; let base = merge(themeTpl.BASE, { - 'font-family': this.opts.fonts, - 'font-size': this.opts.size - }) - let base_block = merge(base, {}) + "font-family": this.opts.fonts, + "font-size": this.opts.size, + }); + let base_block = merge(base, {}); for (let ele in themeTpl.inline) { if (themeTpl.inline.hasOwnProperty(ele)) { - let style = themeTpl.inline[ele] - mapping[ele] = merge(base, style) + let style = themeTpl.inline[ele]; + mapping[ele] = merge(base, style); } } for (let ele in themeTpl.block) { if (themeTpl.block.hasOwnProperty(ele)) { - let style = themeTpl.block[ele] - if (ele === 'code') { - style['font-family'] = CODE_FONT_FAMILY + let style = themeTpl.block[ele]; + if (ele === "code") { + style["font-family"] = CODE_FONT_FAMILY; } - mapping[ele] = merge(base_block, style) + mapping[ele] = merge(base_block, style); } } - return mapping - } + return mapping; + }; let getStyles = (tokenName, addition) => { - let arr = [] - let dict = styleMapping[tokenName] - if (!dict) return '' + let arr = []; + let dict = styleMapping[tokenName]; + if (!dict) return ""; for (const key in dict) { - arr.push(key + ':' + dict[key]) + arr.push(key + ":" + dict[key]); } - return `style="${arr.join(';') + (addition || '')}"` - } + return `style="${arr.join(";") + (addition || "")}"`; + }; let addFootnote = (title, link) => { - footnotes.push([++footnoteIndex, title, link]) - return footnoteIndex - } + footnotes.push([++footnoteIndex, title, link]); + return footnoteIndex; + }; this.buildFootnotes = () => { - let footnoteArray = footnotes.map(x => { + let footnoteArray = footnotes.map((x) => { if (x[1] === x[2]) { - return `[${x[0]}]: ${x[1]}
` + return `[${x[0]}]: ${x[1]}
`; } - return `[${x[0]}] ${x[1]}: ${x[2]}
` - }) - return `

引用链接

${footnoteArray.join('\n')}

` - } + return `[${x[0]}] ${x[1]}: ${x[2]}
`; + }); + return `

引用链接

${footnoteArray.join("\n")}

`; + }; this.buildAddition = () => { return ` @@ -78,99 +81,129 @@ const WxRenderer = function (opts) { font-weight: 600; } - ` - } + `; + }; - this.setOptions = newOpts => { - this.opts = merge(this.opts, newOpts) - } + this.setOptions = (newOpts) => { + this.opts = merge(this.opts, newOpts); + }; - this.hasFootnotes = () => footnotes.length !== 0 + this.hasFootnotes = () => footnotes.length !== 0; this.getRenderer = (status) => { - footnotes = [] - footnoteIndex = 0 + footnotes = []; + footnoteIndex = 0; - styleMapping = this.buildTheme(this.opts.theme) - let renderer = new marked.Renderer() + styleMapping = this.buildTheme(this.opts.theme); + let renderer = new marked.Renderer(); renderer.heading = (text, level) => { switch (level) { case 1: - return `

${text}

` + return `

${text}

`; case 2: - return `

${text}

` + return `

${text}

`; case 3: - return `

${text}

` + return `

${text}

`; default: - return `

${text}

` + return `

${text}

`; } - } - renderer.paragraph = text => { - if (text.indexOf(' { + if (text.indexOf("${text}

` - } + return text.replace(/ /g, "") === "" + ? "" + : `

${text}

`; + }; - renderer.blockquote = text => { - text = text.replace(//g, `

`) - return `

${text}
` - } + renderer.blockquote = (text) => { + text = text.replace(//g, `

`); + return `

${text}
`; + }; renderer.code = (text, lang) => { - text = text.replace(//g, '>') - const codeLines = text.split('\n').map(line => `${(line || '
')}
`) - const codeTheme = 'github' + text = text.replace(//g, ">"); + const codeLines = text + .split("\n") + .map( + (line) => + `${ + line || "
" + }
` + ); + const codeTheme = "github"; return `
-                        ${codeLines.join('')}
+                        ${codeLines.join("")}
                     
- ` - } - renderer.codespan = (text, lang) => `${text}` - renderer.listitem = text => `<%s/>${text}` + `; + }; + renderer.codespan = (text, lang) => + `${text}`; + renderer.listitem = (text) => + `<%s/>${text}`; renderer.list = (text, ordered, start) => { - text = text.replace(/<\/*p.*?>/g, '') - let segments = text.split(`<%s/>`) + text = text.replace(/<\/*p.*?>/g, ""); + let segments = text.split(`<%s/>`); if (!ordered) { - text = segments.join('•') - return `

${text}

` + text = segments.join("•"); + return `

${text}

`; } - text = segments[0] + text = segments[0]; for (let i = 1; i < segments.length; i++) { - text = text + i + '.' + segments[i] + text = text + i + "." + segments[i]; } - return `

${text}

` - } + return `

${text}

`; + }; renderer.image = (href, title, text) => { - let subText = '' + let subText = ""; if (text) { - subText = `
${text}
` + subText = `
${text}
`; } - let figureStyles = getStyles('figure') - let imgStyles = getStyles(ENV_STRETCH_IMAGE ? 'image' : 'image_org') - return `
${text}${subText}
` - } + let figureStyles = getStyles("figure"); + let imgStyles = getStyles( + ENV_STRETCH_IMAGE ? "image" : "image_org" + ); + return `
${text}${subText}
`; + }; renderer.link = (href, title, text) => { - if (href.indexOf('https://mp.weixin.qq.com') === 0) { - return `${text}` + if (href.indexOf("https://mp.weixin.qq.com") === 0) { + return `${text}`; } if (href === text || !status) { - return text + return text; } - let ref = addFootnote(title || text, href) - return `${text}[${ref}]` - } - renderer.strong = text => `${text}` - renderer.em = text => `${text}` - renderer.table = (header, body) => `
${header}${body}
` + let ref = addFootnote(title || text, href); + return `${text}[${ref}]`; + }; + renderer.strong = (text) => + `${text}`; + renderer.em = (text) => + `${text}`; + renderer.table = (header, body) => + `
${header}${body}
`; // renderer.tablerow = (text) => `${text}`; - renderer.tablecell = (text, flags) => `${text}` - renderer.hr = () => `
` - return renderer - } -} -export default WxRenderer + renderer.tablecell = (text, flags) => + `${text}`; + renderer.hr = () => + `
`; + return renderer; + }; +}; +export default WxRenderer; diff --git a/src/assets/scripts/themes/default-theme-css.js b/src/assets/scripts/themes/default-theme-css.js index 1648564..ab14aae 100644 --- a/src/assets/scripts/themes/default-theme-css.js +++ b/src/assets/scripts/themes/default-theme-css.js @@ -1,5 +1,4 @@ -const DEFAULT_CSS_CONTENT = -`/* +const DEFAULT_CSS_CONTENT = `/* 按Ctrl+F可格式化 */ /* 一级标题样式 */ @@ -38,5 +37,5 @@ link { /* 微信链接样式 */ wx_link { } -` -export default DEFAULT_CSS_CONTENT +`; +export default DEFAULT_CSS_CONTENT; diff --git a/src/assets/scripts/themes/default-theme.js b/src/assets/scripts/themes/default-theme.js index aa8eac6..86445fa 100644 --- a/src/assets/scripts/themes/default-theme.js +++ b/src/assets/scripts/themes/default-theme.js @@ -1,178 +1,178 @@ -export default { +export default { BASE: { - 'text-align': 'left', - 'color': '#3f3f3f', - 'line-height': '1.75', + "text-align": "left", + color: "#3f3f3f", + "line-height": "1.75", }, BASE_BLOCK: { - 'margin': '1em 8px' + margin: "1em 8px", }, block: { // 一级标题样式 h1: { - 'font-size': '1.2em', - 'text-align': 'center', - 'font-weight': 'bold', - 'display': 'table', - 'margin': '2em auto 1em', - 'padding': '0 1em', - 'border-bottom': '2px solid rgba(0, 152, 116, 0.9)' + "font-size": "1.2em", + "text-align": "center", + "font-weight": "bold", + display: "table", + margin: "2em auto 1em", + padding: "0 1em", + "border-bottom": "2px solid rgba(0, 152, 116, 0.9)", }, // 二级标题样式 h2: { - 'font-size': '1.2em', - 'text-align': 'center', - 'font-weight': 'bold', - 'display': 'table', - 'margin': '4em auto 2em', - 'padding': '0 0.2em', - 'background': 'rgba(0, 152, 116, 0.9)', - 'color': '#fff' + "font-size": "1.2em", + "text-align": "center", + "font-weight": "bold", + display: "table", + margin: "4em auto 2em", + padding: "0 0.2em", + background: "rgba(0, 152, 116, 0.9)", + color: "#fff", }, // 三级标题样式 h3: { - 'font-weight': 'bold', - 'font-size': '1.1em', - 'margin': '2em 8px 0.75em 0', - 'line-height': '1.2', - 'padding-left': '8px', - 'border-left': '3px solid rgba(0, 152, 116, 0.9)' + "font-weight": "bold", + "font-size": "1.1em", + margin: "2em 8px 0.75em 0", + "line-height": "1.2", + "padding-left": "8px", + "border-left": "3px solid rgba(0, 152, 116, 0.9)", }, // 四级标题样式 h4: { - 'font-weight': 'bold', - 'font-size': '1em', - 'margin': '2em 8px 0.5em', - 'color': 'rgba(66, 185, 131, 0.9)' + "font-weight": "bold", + "font-size": "1em", + margin: "2em 8px 0.5em", + color: "rgba(66, 185, 131, 0.9)", }, // 段落样式 p: { - 'margin': '1.5em 8px', - 'letter-spacing': '0.1em' + margin: "1.5em 8px", + "letter-spacing": "0.1em", }, // 引用样式 blockquote: { - 'font-style': 'normal', - 'border-left': 'none', - 'padding': '1em', - 'border-radius': '4px', - 'color': '#FEEEED', - 'background': 'rgba(27,31,35,.05)', - 'margin': '2em 8px' + "font-style": "normal", + "border-left": "none", + padding: "1em", + "border-radius": "4px", + color: "#FEEEED", + background: "rgba(27,31,35,.05)", + margin: "2em 8px", }, blockquote_p: { - 'letter-spacing': '0.1em', - 'color': 'rgb(80, 80, 80)', - 'font-size': '1em', - 'display': 'block', + "letter-spacing": "0.1em", + color: "rgb(80, 80, 80)", + "font-size": "1em", + display: "block", }, code: { - 'font-size': '80%', - 'overflow': 'auto', - 'color': '#333', - 'white-space': 'pre', - 'background': 'rgb(247, 247, 247)', - 'border-radius': '2px', - 'padding': '10px', - 'line-height': '1.5', - 'border': '1px solid rgb(236,236,236)', - 'margin': '20px 0', + "font-size": "80%", + overflow: "auto", + color: "#333", + "white-space": "pre", + background: "rgb(247, 247, 247)", + "border-radius": "2px", + padding: "10px", + "line-height": "1.5", + border: "1px solid rgb(236,236,236)", + margin: "20px 0", }, image: { - 'border-radius': '4px', - 'display': 'block', - 'margin': '0.1em auto 0.5em', - 'width': '100% !important', + "border-radius": "4px", + display: "block", + margin: "0.1em auto 0.5em", + width: "100% !important", }, image_org: { - 'border-radius': '4px', - 'display': 'block' + "border-radius": "4px", + display: "block", }, ol: { - 'margin-left': '0', - 'padding-left': '1em' + "margin-left": "0", + "padding-left": "1em", }, ul: { - 'margin-left': '0', - 'padding-left': '1em', - 'list-style': 'circle' + "margin-left": "0", + "padding-left": "1em", + "list-style": "circle", }, footnotes: { - 'margin': '0.5em 8px', - 'font-size': '80%' + margin: "0.5em 8px", + "font-size": "80%", }, figure: { - 'margin': '1.5em 8px', - } + margin: "1.5em 8px", + }, }, inline: { listitem: { - 'text-indent': '-1em', - 'display': 'block', - 'margin': '0.2em 8px' + "text-indent": "-1em", + display: "block", + margin: "0.2em 8px", }, codespan: { - 'font-size': '90%', - 'white-space': 'pre', - 'color': '#d14', - 'background': 'rgba(27,31,35,.05)', - 'padding': '3px 5px', - 'border-radius': '4px', + "font-size": "90%", + "white-space": "pre", + color: "#d14", + background: "rgba(27,31,35,.05)", + padding: "3px 5px", + "border-radius": "4px", }, link: { - 'color': '#576b95' + color: "#576b95", }, wx_link: { - 'color': '#576b95', - 'text-decoration': 'none', + color: "#576b95", + "text-decoration": "none", }, // 字体加粗样式 strong: { - 'color': 'rgba(15, 76, 129, 0.9)', - 'font-weight': 'bold', + color: "rgba(15, 76, 129, 0.9)", + "font-weight": "bold", }, table: { - 'border-collapse': 'collapse', - 'text-align': 'center', - 'margin': '1em 8px' + "border-collapse": "collapse", + "text-align": "center", + margin: "1em 8px", }, thead: { - 'background': 'rgba(0, 0, 0, 0.05)', - 'font-weight': 'bold' + background: "rgba(0, 0, 0, 0.05)", + "font-weight": "bold", }, td: { - 'border': '1px solid #dfdfdf', - 'padding': '0.25em 0.5em' + border: "1px solid #dfdfdf", + padding: "0.25em 0.5em", }, footnote: { - 'font-size': '12px' + "font-size": "12px", }, figcaption: { - 'text-align': 'center', - 'color': '#888', - 'font-size': '0.8em' - } - } + "text-align": "center", + color: "#888", + "font-size": "0.8em", + }, + }, }; diff --git a/src/assets/scripts/uploadImageFile.js b/src/assets/scripts/uploadImageFile.js index b95b27e..da77874 100644 --- a/src/assets/scripts/uploadImageFile.js +++ b/src/assets/scripts/uploadImageFile.js @@ -1,4 +1,4 @@ -import fileApi from '../../api/file'; +import fileApi from "../../api/file"; export function uploadImgFile(file) { return new Promise((resolve, reject) => { @@ -12,22 +12,25 @@ export function uploadImgFile(file) { base64Reader.readAsDataURL(file); base64Reader.onload = function () { - const base64Content = this.result.split(',').pop(); - fileApi.fileUpload(base64Content, file).then(res => { - resolve(res); - }).catch(err => { - reject(err); - }) + const base64Content = this.result.split(",").pop(); + fileApi + .fileUpload(base64Content, file) + .then((res) => { + resolve(res); + }) + .catch((err) => { + reject(err); + }); }; }); } export function isImageIllegal(file) { if (!/\.(gif|jpg|jpeg|png|GIF|JPG|PNG)$/.test(file.name)) { - return '请上传 JPG/PNG/GIF 格式的图片'; + return "请上传 JPG/PNG/GIF 格式的图片"; } if (file.size > 5 * 1024 * 1024) { - return '由于公众号限制,图片大小不能超过 5.0M'; + return "由于公众号限制,图片大小不能超过 5.0M"; } return false; } diff --git a/src/assets/scripts/util.js b/src/assets/scripts/util.js index 2c326f9..4fed100 100644 --- a/src/assets/scripts/util.js +++ b/src/assets/scripts/util.js @@ -1,95 +1,94 @@ -import default_theme from './themes/default-theme' -import prettier from 'prettier/standalone' -import prettierMarkdown from 'prettier/parser-markdown' - +import default_theme from "./themes/default-theme"; +import prettier from "prettier/standalone"; +import prettierMarkdown from "prettier/parser-markdown"; // 设置自定义颜色 export function setColorWithTemplate(template) { return function (color) { - let custom_theme = JSON.parse(JSON.stringify(template)) - custom_theme.block.h1['border-bottom'] = `2px solid ${color}` - custom_theme.block.h2['background'] = color - custom_theme.block.h3['border-left'] = `3px solid ${color}` - custom_theme.block.h4['color'] = color - custom_theme.inline.strong['color'] = color - return custom_theme - } + let custom_theme = JSON.parse(JSON.stringify(template)); + custom_theme.block.h1["border-bottom"] = `2px solid ${color}`; + custom_theme.block.h2["background"] = color; + custom_theme.block.h3["border-left"] = `3px solid ${color}`; + custom_theme.block.h4["color"] = color; + custom_theme.inline.strong["color"] = color; + return custom_theme; + }; } export const setColorWithCustomTemplate = function setColorWithCustomTemplate( template, color ) { - let custom_theme = JSON.parse(JSON.stringify(template)) - custom_theme.block.h1['border-bottom'] = `2px solid ${color}` - custom_theme.block.h2['background'] = color - custom_theme.block.h3['border-left'] = `3px solid ${color}` - custom_theme.block.h4['color'] = color - custom_theme.inline.strong['color'] = color - return custom_theme -} + let custom_theme = JSON.parse(JSON.stringify(template)); + custom_theme.block.h1["border-bottom"] = `2px solid ${color}`; + custom_theme.block.h2["background"] = color; + custom_theme.block.h3["border-left"] = `3px solid ${color}`; + custom_theme.block.h4["color"] = color; + custom_theme.inline.strong["color"] = color; + return custom_theme; +}; // 设置自定义字体大小 export function setFontSizeWithTemplate(template) { return function (fontSize) { - let custom_theme = JSON.parse(JSON.stringify(template)) - custom_theme.block.h1['font-size'] = `${fontSize * 1.14}px` - custom_theme.block.h2['font-size'] = `${fontSize * 1.1}px` - custom_theme.block.h3['font-size'] = `${fontSize}px` - custom_theme.block.h4['font-size'] = `${fontSize}px` - return custom_theme - } + let custom_theme = JSON.parse(JSON.stringify(template)); + custom_theme.block.h1["font-size"] = `${fontSize * 1.14}px`; + custom_theme.block.h2["font-size"] = `${fontSize * 1.1}px`; + custom_theme.block.h3["font-size"] = `${fontSize}px`; + custom_theme.block.h4["font-size"] = `${fontSize}px`; + return custom_theme; + }; } -export const setColor = setColorWithTemplate(default_theme) -export const setFontSize = setFontSizeWithTemplate(default_theme) +export const setColor = setColorWithTemplate(default_theme); +export const setFontSize = setFontSizeWithTemplate(default_theme); export function customCssWithTemplate(jsonString, color, theme) { - let custom_theme = JSON.parse(JSON.stringify(theme)) + let custom_theme = JSON.parse(JSON.stringify(theme)); // block - custom_theme.block.h1['border-bottom'] = `2px solid ${color}` - custom_theme.block.h2['background'] = color - custom_theme.block.h3['border-left'] = `3px solid ${color}` - custom_theme.block.h4['color'] = color - custom_theme.inline.strong['color'] = color + custom_theme.block.h1["border-bottom"] = `2px solid ${color}`; + custom_theme.block.h2["background"] = color; + custom_theme.block.h3["border-left"] = `3px solid ${color}`; + custom_theme.block.h4["color"] = color; + custom_theme.inline.strong["color"] = color; - custom_theme.block.h1 = Object.assign(custom_theme.block.h1, jsonString.h1) - custom_theme.block.h2 = Object.assign(custom_theme.block.h2, jsonString.h2) - custom_theme.block.h3 = Object.assign(custom_theme.block.h3, jsonString.h3) - custom_theme.block.h4 = Object.assign(custom_theme.block.h4, jsonString.h4) - custom_theme.block.p = Object.assign(custom_theme.block.p, jsonString.p) + custom_theme.block.h1 = Object.assign(custom_theme.block.h1, jsonString.h1); + custom_theme.block.h2 = Object.assign(custom_theme.block.h2, jsonString.h2); + custom_theme.block.h3 = Object.assign(custom_theme.block.h3, jsonString.h3); + custom_theme.block.h4 = Object.assign(custom_theme.block.h4, jsonString.h4); + custom_theme.block.p = Object.assign(custom_theme.block.p, jsonString.p); custom_theme.block.blockquote = Object.assign( custom_theme.block.blockquote, jsonString.blockquote - ) + ); custom_theme.block.blockquote_p = Object.assign( custom_theme.block.blockquote_p, jsonString.blockquote_p - ) + ); custom_theme.block.image = Object.assign( custom_theme.block.image, jsonString.image - ) + ); // inline custom_theme.inline.strong = Object.assign( custom_theme.inline.strong, jsonString.strong - ) + ); custom_theme.inline.codespan = Object.assign( custom_theme.inline.codespan, jsonString.codespan - ) + ); custom_theme.inline.link = Object.assign( custom_theme.inline.link, jsonString.link - ) + ); custom_theme.inline.wx_link = Object.assign( custom_theme.inline.wx_link, jsonString.wx_link - ) + ); - return custom_theme + return custom_theme; } /** @@ -101,19 +100,23 @@ export function css2json(css) { // 移除CSS所有注释 let open, close; while ( - (open = css.indexOf('/*')) !== -1 && - (close = css.indexOf('*/')) !== -1 + (open = css.indexOf("/*")) !== -1 && + (close = css.indexOf("*/")) !== -1 ) { - css = css.substring(0, open) + css.substring(close + 2) + css = css.substring(0, open) + css.substring(close + 2); } // 初始化返回值 - let json = {} + let json = {}; - while (css.length > 0 && css.indexOf('{') !== -1 && css.indexOf('}') !== -1) { + while ( + css.length > 0 && + css.indexOf("{") !== -1 && + css.indexOf("}") !== -1 + ) { // 存储第一个左/右花括号的下标 - const lbracket = css.indexOf('{') - const rbracket = css.indexOf('}') + const lbracket = css.indexOf("{"); + const rbracket = css.indexOf("}"); // 第一步:将声明转换为Object,如: // `font: 'Times New Roman' 1em; color: #ff0000; margin-top: 1em;` @@ -122,25 +125,25 @@ export function css2json(css) { // 辅助方法:将array转为object function toObject(array) { - let ret = {} - array.forEach(e => { - const index = e.indexOf(':') - const property = e.substring(0, index).trim() - const value = e.substring(index + 1).trim() - ret[property] = value - }) - return ret + let ret = {}; + array.forEach((e) => { + const index = e.indexOf(":"); + const property = e.substring(0, index).trim(); + const value = e.substring(index + 1).trim(); + ret[property] = value; + }); + return ret; } // 切割声明块并移除空白符,然后放入数组中 let declarations = css .substring(lbracket + 1, rbracket) - .split(';') - .map(e => e.trim()) - .filter(e => e.length > 0) // 移除所有""空值 + .split(";") + .map((e) => e.trim()) + .filter((e) => e.length > 0); // 移除所有""空值 // 转为Object对象 - declarations = toObject(declarations) + declarations = toObject(declarations); // 第二步:选择器处理,每个选择器会与它对应的声明相关联,如: // `h1, p#bar {color: red}` @@ -150,65 +153,63 @@ export function css2json(css) { let selectors = css .substring(0, lbracket) // 以,切割,并移除空格:`"h1, p#bar, span.foo"` => ["h1", "p#bar", "span.foo"] - .split(',') - .map(selector => selector.trim()) + .split(",") + .map((selector) => selector.trim()); // 迭代赋值 - selectors.forEach(selector => { + selectors.forEach((selector) => { // 若不存在,则先初始化 - if (!json[selector]) json[selector] = {} + if (!json[selector]) json[selector] = {}; // 赋值到JSON - Object.keys(declarations).forEach(key => { - json[selector][key] = declarations[key] - }) - }) + Object.keys(declarations).forEach((key) => { + json[selector][key] = declarations[key]; + }); + }); // 继续下个声明块 - css = css.slice(rbracket + 1).trim() + css = css.slice(rbracket + 1).trim(); } // 返回JSON形式的结果串 - return json + return json; } - /** * 将编辑器内容保存到 LocalStorage - * @param {*} editor - * @param {*} name + * @param {*} editor + * @param {*} name */ export function saveEditorContent(editor, name) { - const content = editor.getValue(0) + const content = editor.getValue(0); if (content) { - localStorage.setItem(name, content) + localStorage.setItem(name, content); } else { - localStorage.removeItem(name) + localStorage.removeItem(name); } } - export function formatDoc(content) { const doc = prettier.format(content, { - parser: 'markdown', - plugins: [prettierMarkdown] - }) - return doc + parser: "markdown", + plugins: [prettierMarkdown], + }); + return doc; } -export function fixCodeWhiteSpace(value = 'pre') { - const preDomList = document.getElementsByClassName('code__pre'); +export function fixCodeWhiteSpace(value = "pre") { + const preDomList = document.getElementsByClassName("code__pre"); if (preDomList.length > 0) { - preDomList.forEach(pre => { + preDomList.forEach((pre) => { pre.style.whiteSpace = value; - }) + }); } } export function downLoadMD(doc) { - let downLink = document.createElement('a'); + let downLink = document.createElement("a"); - downLink.download = 'content.md'; - downLink.style.display = 'none'; + downLink.download = "content.md"; + downLink.style.display = "none"; let blob = new Blob([doc]); downLink.href = URL.createObjectURL(blob); @@ -223,7 +224,7 @@ export function downLoadMD(doc) { * @param {*} rows 行 * @param {*} cols 列 */ -export function createTable({data, rows, cols}) { +export function createTable({ data, rows, cols }) { let table = ""; let currRow = []; for (let i = 0; i < rows + 2; ++i) { @@ -231,13 +232,13 @@ export function createTable({data, rows, cols}) { currRow = []; for (let j = 0; j < cols; ++j) { const rowIdx = i > 1 ? i - 1 : i; - i === 1 ? - currRow.push("---\t") : - currRow.push(data[`k_${rowIdx}_${j}`] || ""); + i === 1 + ? currRow.push("---\t") + : currRow.push(data[`k_${rowIdx}_${j}`] || ""); } table += currRow.join("\t|\t"); table += "\t|\n"; } return table; -} \ No newline at end of file +} diff --git a/src/main.js b/src/main.js index 591fd47..575cfb1 100644 --- a/src/main.js +++ b/src/main.js @@ -1,27 +1,27 @@ -import Vue from 'vue' -import App from './App.vue' -import store from './store' -import ElementUI from 'element-ui' -import 'element-ui/lib/theme-chalk/index.css' -import './plugins/element' -import 'codemirror/lib/codemirror.css'; -import 'codemirror/theme/ambiance.css'; -import 'codemirror/theme/xq-light.css'; -import 'codemirror/mode/css/css' -import 'codemirror/mode/markdown/markdown' -import 'codemirror/addon/edit/matchbrackets' -import 'codemirror/addon/selection/active-line' -import 'codemirror/addon/hint/show-hint.js' -import 'codemirror/addon/hint/css-hint.js' -import './assets/less/theme.less'; +import Vue from "vue"; +import App from "./App.vue"; +import store from "./store"; +import ElementUI from "element-ui"; +import "element-ui/lib/theme-chalk/index.css"; +import "./plugins/element"; +import "codemirror/lib/codemirror.css"; +import "codemirror/theme/ambiance.css"; +import "codemirror/theme/xq-light.css"; +import "codemirror/mode/css/css"; +import "codemirror/mode/markdown/markdown"; +import "codemirror/addon/edit/matchbrackets"; +import "codemirror/addon/selection/active-line"; +import "codemirror/addon/hint/show-hint.js"; +import "codemirror/addon/hint/css-hint.js"; +import "./assets/less/theme.less"; // 对codemirror预处理 -import './assets/scripts/format' -import './assets/scripts/closebrackets' -Vue.use(ElementUI) +import "./assets/scripts/format"; +import "./assets/scripts/closebrackets"; +Vue.use(ElementUI); -Vue.config.productionTip = false +Vue.config.productionTip = false; new Vue({ store, - render: h => h(App) -}).$mount('#app') + render: (h) => h(App), +}).$mount("#app"); diff --git a/src/plugins/element/index.js b/src/plugins/element/index.js index 67bbdea..7b29075 100644 --- a/src/plugins/element/index.js +++ b/src/plugins/element/index.js @@ -1,4 +1,4 @@ -import Vue from 'vue' +import Vue from "vue"; import { Container, Header, @@ -16,8 +16,8 @@ import { Row, Dialog, Loading, - Message -} from 'element-ui' + Message, +} from "element-ui"; Vue.use(Container); Vue.use(Header); diff --git a/src/store/index.js b/src/store/index.js index e1d138c..a58b5c4 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,31 +1,28 @@ -import Vue from 'vue' -import Vuex from 'vuex' -import config from '../assets/scripts/config'; -import WxRenderer from '../assets/scripts/renderers/wx-renderer' -import marked from 'marked' -import CodeMirror from 'codemirror/lib/codemirror' -import DEFAULT_CONTENT from '../assets/scripts/default-content' -import DEFAULT_CSS_CONTENT from '../assets/scripts/themes/default-theme-css' -import { - setColor, - formatDoc -} from '../assets/scripts/util' +import Vue from "vue"; +import Vuex from "vuex"; +import config from "../assets/scripts/config"; +import WxRenderer from "../assets/scripts/renderers/wx-renderer"; +import marked from "marked"; +import CodeMirror from "codemirror/lib/codemirror"; +import DEFAULT_CONTENT from "../assets/scripts/default-content"; +import DEFAULT_CSS_CONTENT from "../assets/scripts/themes/default-theme-css"; +import { setColor, formatDoc } from "../assets/scripts/util"; -Vue.use(Vuex) +Vue.use(Vuex); const state = { wxRenderer: null, - output: '', - html: '', + output: "", + html: "", editor: null, cssEditor: null, - currentFont: '', - currentSize: '', - currentColor: '', + currentFont: "", + currentSize: "", + currentColor: "", citeStatus: 0, nightMode: false, - codeTheme: 'github', - rightClickMenuVisible: false + codeTheme: "github", + rightClickMenuVisible: false, }; const mutations = { setEditorValue(state, data) { @@ -39,101 +36,116 @@ const mutations = { }, setCiteStatus(state, data) { state.citeStatus = data; - localStorage.setItem('citeStatus', data); + localStorage.setItem("citeStatus", data); }, setCurrentFont(state, data) { state.currentFont = data; - localStorage.setItem('fonts', data); + localStorage.setItem("fonts", data); }, setCurrentSize(state, data) { state.currentSize = data; - localStorage.setItem('size', data); + localStorage.setItem("size", data); }, setCurrentColor(state, data) { state.currentColor = data; - localStorage.setItem('color', data); + localStorage.setItem("color", data); }, setCurrentCodeTheme(state, data) { state.codeTheme = data; - localStorage.setItem('codeTheme', data); + localStorage.setItem("codeTheme", data); }, setRightClickMenuVisible(state, data) { state.rightClickMenuVisible = data; }, themeChanged(state) { state.nightMode = !state.nightMode; - localStorage.setItem('nightMode', state.nightMode); + localStorage.setItem("nightMode", state.nightMode); }, initEditorState(state) { - state.currentFont = localStorage.getItem('fonts') || config.builtinFonts[0].value; - state.currentColor = localStorage.getItem('color') || config.colorOption[1].value; - state.currentSize = localStorage.getItem('size') || config.sizeOption[2].value; - state.codeTheme = localStorage.getItem('codeTheme') || config.codeThemeOption[0].value; - state.citeStatus = localStorage.getItem('citeStatus') === 'true'; - state.nightMode = localStorage.getItem('nightMode') === 'true'; + state.currentFont = + localStorage.getItem("fonts") || config.builtinFonts[0].value; + state.currentColor = + localStorage.getItem("color") || config.colorOption[1].value; + state.currentSize = + localStorage.getItem("size") || config.sizeOption[2].value; + state.codeTheme = + localStorage.getItem("codeTheme") || + config.codeThemeOption[0].value; + state.citeStatus = localStorage.getItem("citeStatus") === "true"; + state.nightMode = localStorage.getItem("nightMode") === "true"; state.wxRenderer = new WxRenderer({ theme: setColor(state.currentColor), fonts: state.currentFont, size: state.currentSize, - status: state.citeStatus + status: state.citeStatus, }); }, initEditorEntity(state) { state.editor = CodeMirror.fromTextArea( - document.getElementById('editor'), { - value: '', - mode: 'text/x-markdown', - theme: 'xq-light', + document.getElementById("editor"), + { + value: "", + mode: "text/x-markdown", + theme: "xq-light", lineNumbers: false, lineWrapping: true, styleActiveLine: true, autoCloseBrackets: true, extraKeys: { - 'Ctrl-F': function autoFormat(editor) { - const doc = formatDoc(editor.getValue(0)) - localStorage.setItem('__editor_content', doc) - editor.setValue(doc) + "Ctrl-F": function autoFormat(editor) { + const doc = formatDoc(editor.getValue(0)); + localStorage.setItem("__editor_content", doc); + editor.setValue(doc); }, - 'Ctrl-S': function save(editor) {} - } + "Ctrl-S": function save(editor) {}, + }, } ); - + // 如果有编辑器内容被保存则读取,否则加载默认内容 - state.editor.setValue(localStorage.getItem('__editor_content') || formatDoc(DEFAULT_CONTENT)) + state.editor.setValue( + localStorage.getItem("__editor_content") || + formatDoc(DEFAULT_CONTENT) + ); }, initCssEditorEntity(state) { state.cssEditor = CodeMirror.fromTextArea( - document.getElementById('cssEditor'), { - value: '', - mode: 'css', - theme: 'style-mirror', + document.getElementById("cssEditor"), + { + value: "", + mode: "css", + theme: "style-mirror", lineNumbers: false, lineWrapping: true, matchBrackets: true, autofocus: true, extraKeys: { - 'Ctrl-F': function autoFormat(editor) { + "Ctrl-F": function autoFormat(editor) { const totalLines = editor.lineCount(); - editor.autoFormatRange({ - line: 0, - ch: 0 - }, { - line: totalLines - }); + editor.autoFormatRange( + { + line: 0, + ch: 0, + }, + { + line: totalLines, + } + ); }, - 'Ctrl-S': function save(editor) {} - } + "Ctrl-S": function save(editor) {}, + }, } ); // 如果有编辑器内容被保存则读取,否则加载默认内容 - state.cssEditor.setValue(localStorage.getItem('__css_content') || DEFAULT_CSS_CONTENT) + state.cssEditor.setValue( + localStorage.getItem("__css_content") || DEFAULT_CSS_CONTENT + ); }, editorRefresh(state) { let output = marked(state.editor.getValue(0), { - renderer: state.wxRenderer.getRenderer(state.citeStatus) + renderer: state.wxRenderer.getRenderer(state.citeStatus), }); // 去除第一行的 margin-top @@ -151,11 +163,11 @@ const mutations = { state.editor.setValue(doc); state.cssEditor.setValue(DEFAULT_CSS_CONTENT); - } -} + }, +}; export default new Vuex.Store({ state, mutations, - actions: {} + actions: {}, }); diff --git a/vue.config.js b/vue.config.js index ab81f53..cc4c11d 100644 --- a/vue.config.js +++ b/vue.config.js @@ -1,4 +1,4 @@ module.exports = { - outputDir: 'dist', - publicPath: '/md/' -} \ No newline at end of file + outputDir: "dist", + publicPath: "/md/", +};