[
  {
    "path": ".gitignore",
    "content": "# Created by .ignore support plugin (hsz.mobi)\n### Java template\n# Compiled class file\n*.class\n\n# Log file\n*.log\n\n# BlueJ files\n*.ctxt\n\n# Mobile Tools for Java (J2ME)\n.mtj.tmp/\n\n# Package Files #\n*.jar\n*.war\n*.nar\n*.ear\n*.zip\n*.tar.gz\n*.rar\n\n# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml\nhs_err_pid*\n\n### JetBrains template\n.idea\n\n### Android template\n# Built application files\n*.apk\n*.ap_\n*.aab\n\n# Files for the ART/Dalvik VM\n*.dex\n\n# Java class files\n*.class\n\n# Generated files\nbin/\ngen/\nout/\nrelease/\n\n# Gradle files\n.gradle/\nbuild/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n# Proguard folder generated by Eclipse\nproguard/\n\n# Log Files\n*.log\n\n# Android Studio Navigation editor temp files\n.navigation/\n\n# Android Studio captures folder\ncaptures/\n\n# IntelliJ\n*.iml\n.idea/workspace.xml\n.idea/tasks.xml\n.idea/gradle.xml\n.idea/assetWizardSettings.xml\n.idea/dictionaries\n.idea/libraries\n# Android Studio 3 in .gitignore file.\n.idea/caches\n.idea/modules.xml\n# Comment next line if keeping position of elements in Navigation Editor is relevant for you\n.idea/navEditor.xml\n\n# Keystore files\n# Uncomment the following lines if you do not want to check your keystore files in.\n#*.jks\n#*.keystore\n\n# External native build folder generated in Android Studio 2.2 and later\n.externalNativeBuild\n\n# Google Services (e.g. APIs or Firebase)\n# google-services.json\n\n# Freeline\nfreeline.py\nfreeline/\nfreeline_project_description.json\n\n# fastlane\nfastlane/report.xml\nfastlane/Preview.html\nfastlane/screenshots\nfastlane/test_output\nfastlane/readme.md\n\n# Version control\nvcs.xml\n\n# lint\nlint/intermediates/\nlint/generated/\nlint/outputs/\nlint/tmp/\n# lint/reports/\n\n### macOS template\n# General\n.DS_Store\n.AppleDouble\n.LSOverride\n\n# Icon must end with two \\r\nIcon\n\n# Thumbnails\n._*\n\n# Files that might appear in the root of a volume\n.DocumentRevisions-V100\n.fseventsd\n.Spotlight-V100\n.TemporaryItems\n.Trashes\n.VolumeIcon.icns\n.com.apple.timemachine.donotpresent\n\n# Directories potentially created on remote AFP share\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk\n\n.idea/markdown-navigator/\n.idea/misc.xml\n"
  },
  {
    "path": "README.md",
    "content": "# 微信公众号 Markdown 编辑器\n\n这款编辑器可以将 Markdown 转换成微信公众号编辑器的样式，可以直接复制到公众号后台。\n\n这让你在公众号创作时，把更多的时间专注于文章本身，而不是繁琐地调整文章样式。\n\n[在线使用](http://prod.zkqiang.cn/wxeditor)\n\n## 功能\n\n- 支持序号列表和圆点列表，解决了样式会被重置的问题\n- 外链会自动转换为参考文献索引，并且附在文章末尾\n- 支持多种字体和样式\n- 支持日语注音假名、汉语拼音样式\n- 支持不同于微信的代码配色方案\n\n## 关于\n\n本仓库 Fork 自 [yricat/wechat-format](https://github.com/lyricat/wechat-format)，并根据自用需求进行修改开发。\n\n感谢他的创意和贡献！\n"
  },
  {
    "path": "src/assets/css/app.css",
    "content": "* {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n\ninput, button, textarea {\n  font-family: inherit;\n}\n\nh1, h2, h3, h4, h5, h6 {\n  font-weight: normal;\n}\n\nem {\n  font-style: normal !important;\n}\n\nhtml, body {\n  height: 100%;\n  font-family: 'PingFang SC', BlinkMacSystemFont, Roboto, 'Helvetica Neue', sans-serif;\n}\n\n.copy-button {\n  text-decoration: none;\n  color: #ff3502\n}\n\n.el-message__icon {\n  display: none\n}\n\n.container {\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n}\n\n.top {\n  height: 60px;\n  padding: 10px 20px;\n  display: flex;\n  align-items: center;\n}\n\n.top {\n  margin-right: 20px;\n}\n\n.web-title {\n  margin: 0 15px 0 5px;\n}\n\n.web-icon {\n  width: auto;\n  height: 1.5rem;\n  vertical-align: middle;\n}\n\n#editor {\n  height: 100%;\n  display: block;\n  border: none;\n  width: 100%;\n  padding: 10px;\n}\n\nsection {\n  height: 100%;\n}\n\n.main-body {\n  display: flex;\n  flex-direction: column;\n  padding-top: 0;\n  padding-bottom: 10px;\n}\n\n.ctrl {\n  flex-basis: 60px;\n  flex-grow: 1;\n  flex-shrink: 1;\n  display: flex;\n  align-items: center;\n}\n\n.preview-wrapper {\n  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);\n  padding: 0;\n  align-items: center;\n  justify-content: center;\n  display: flex;\n  /* height: 100%; */\n  overflow: scroll;\n}\n\n.main-section {\n  display: flex;\n  height: 100%;\n}\n\n.hint {\n  opacity: 0.6;\n  margin: 20px 0;\n}\n\n.preview {\n  margin: 0 -20px;\n  width: 375px;\n  padding: 20px;\n  font-size: 14px;\n  outline: none;\n  box-shadow: 0 0 60px rgba(0, 0, 0, 0.1);\n}\n\n.preview table {\n  margin-bottom: 10px;\n  border-collapse: collapse;\n  display: table;\n  width: 100% !important;\n}\n\n/*.preview ul, .preview ol {*/\n/*  padding-left: 40px !important;*/\n/*}*/\n\n.select-item-left {\n  float: left;\n}\n\n.select-item-right {\n  float: right;\n  color: #8492a6;\n  font-size: 13px;\n}\n\n.CodeMirror {\n  height: 100%;\n  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);\n  font-size: 14px;\n  padding: 20px;\n  width: 100%;\n  font-family: 'PingFang SC', BlinkMacSystemFont, Roboto, 'Helvetica Neue', sans-serif;\n}\n\n/* ele ui */\n.el-form-item {\n  margin-bottom: 0 !important;\n}\n\n/*wechat code block*/\n.rich_media_content .code-snippet *, .rich_media_content .code-snippet__fix * {\n  max-width: 1000% !important;\n}\n\n.code-snippet__fix {\n  word-wrap: break-word !important;\n  ont-size: 14px;\n  margin: 10px 0;\n  color: #333;\n  position: relative;\n  background-color: rgba(0, 0, 0, 0.03);\n  border: 1px solid #f0f0f0;\n  border-radius: 2px;\n  display: flex;\n  line-height: 26px;\n}\n\n.code-snippet__fix .code-snippet__line-index {\n  counter-reset: line;\n  flex-shrink: 0;\n  height: 100%;\n  padding: 1em;\n  list-style-type: none;\n}\n\n.code-snippet__fix .code-snippet__line-index li {\n  list-style-type: none;\n  text-align: right;\n}\n\n.code-snippet__fix .code-snippet__line-index li::before {\n  min-width: 1.5em;\n  text-align: right;\n  left: -2.5em;\n  counter-increment: line;\n  content: counter(line);\n  display: inline;\n  color: rgba(0, 0, 0, 0.15);\n}\n\n.code-snippet__fix pre {\n  overflow-x: auto;\n  padding: 1em 1em 1em 1em;\n  white-space: normal;\n  flex: 1;\n  -webkit-overflow-scrolling: touch;\n}\n\n.code-snippet__fix code {\n  text-align: left;\n  font-size: 14px;\n  white-space: pre;\n  display: flex;\n  position: relative;\n  font-family: Consolas, \"Liberation Mono\", Menlo, Courier, monospace;\n}\n"
  },
  {
    "path": "src/assets/css/loading.css",
    "content": ".loading {\n  text-align: center;\n  position: fixed;\n  width: 100%;\n  height: 100%;\n  overflow: hidden;\n  z-index: 99999;\n  background-color: #f2f2f2;\n}\n\n.loading-wrapper {\n  position: fixed;\n  top: 50%;\n  left: 50%;\n  -webkit-transform: translateX(-50%) translateY(-50%);\n  -moz-transform: translateX(-50%) translateY(-50%);\n  -ms-transform: translateX(-50%) translateY(-50%);\n  transform: translateX(-50%) translateY(-50%);\n}\n\n.loading-text {\n  line-height: 1.4;\n  font-size: 1.2rem;\n  font-weight: bold;\n  margin-bottom: 1rem;\n}\n\n.loading-anim {\n  width: 35px;\n  height: 35px;\n  border: 5px solid rgba(189, 189, 189, 0.25);\n  border-left-color: rgba(3, 155, 229, 1);\n  border-top-color: rgba(3, 155, 229, 1);\n  border-radius: 50%;\n  display: inline-block;\n  animation: rotate 600ms infinite linear;\n}\n\n@keyframes rotate {\n  to {\n    transform: rotate(1turn)\n  }\n}\n"
  },
  {
    "path": "src/assets/default-content.md",
    "content": "# 公众号 Markdown 编辑器\n\n### 简介\n\n这款编辑器可以将 Markdown 转换成微信公众号编辑器的样式，只需将 MD 文档复制到左侧栏，再在右侧栏顶部\"点击复制\"，右侧预览内容就可被复制到公众号后台。\n\n这让你在公众号创作时，把更多的时间专注于文章本身，而不是繁琐地调整文章样式。\n\n\n### 功能\n\n- 支持序号列表和圆点列表，解决了样式会被重置的问题\n- 外链会自动转换为参考文献索引，并且附在文章末尾\n- 支持多种字体和样式\n- 支持日语注音假名、汉语拼音样式\n- 支持不同于微信的代码配色方案\n- 支持编辑内容自动保存、预览同步滚动等常见功能\n\n### 关于 Markdown\n\n1. Markdown 是一种轻量级标记语言，能将文本换成有效的 XHTML(或者HTML) 文档\n2. Markdown 强大之处，在于可以用一套格式，在所有支持 Markdown 的编辑器中转换成发布样式，做到最大化兼容，不需要担心复制到不同编辑器中样式被破坏\n3. 正如你右侧看到的这样，Markdown 被转换成了微信支持的样式，同样你可以在一字不改的情况下，在 Github 等平台上转换类似的样式\n4. 学习 Markdown 的语法，可以查看 [Markdown 语法入门手册](https://www.w3cschool.cn/markdownyfsm/markdownyfsm-odm6256r.html)\n\n## 更多样式\n\n### 注音符号\n\n[注音符号 W3C 定义](http://www.w3.org/TR/ruby/)。\n\n支持日语注音假名、汉语拼音。\n\n用法有以下几种：\n\n* 世界{せかい}\n* 小夜時雨{さ・よ・しぐれ}\n* 食べる{たべる}\n* 丧心病狂{gàn・de・piào・liang}\n\n### 图片\n\n接下来是一张图片。你可以用自己图床，也可以上传到微信媒体库再把图片 URL\n粘贴回来，或者编辑好以后，在公众号里插入图片。\n\n![这里可以写图片描述](https://static.zkqiang.cn/images/20191019181145.JPG-slim)\n\n如果使用图床链接的话，有可能复制后图片不能被上传，需要手动在微信重新上传替换。\n\n### 代码块\n\n代码高亮使用了 Github 配色方案，后续会加入更多配色。\n\n**注意：由于微信编辑器限制，复制后若在微信编辑器中点击代码块，会被微信自动重置后它的配色，只能重新再复制**\n\n```cpp\n#include <stdio.h>\n\nconst int MAX = 10;\nint cache[MAX] = {0};\n\nint fib(int x) {\n  if (x == 1) return 1;\n  if (x == 0) return 0;\n  if (cache[x] == 0) {\n    int ret = fib(x - 1) + fib(x - 2);\n    cache[x] = ret;\n  }\n  return cache[x];\n}\n\nint main() {\n    int i;\n    printf(\"fibonacci series:\\n\");\n    for (i = 0; i < MAX; ++i) {\n        printf(\"%d \", fib(i));\n    }\n    return 0;\n}\n```\n\n### 内联代码\n\ninline code `{code: 0}`\n\n### 表格\n\n表格无法使用自定义样式，暂时没找到解决途径\n\n| Header 1 | Header 2 |\n| --- | --- |\n| Key 1 | Value 1 |\n| Key 2 | Value 2 |\n| Key 3 | Value 3 |\n\n### 超链接\n\n如果是公众号文章的超链接，是可以点击打开的，但其他链接都无法点击，所以这里使用类似于文献的底部引用。\n\n例如：\n\n[这是一篇公众号文章](https://mp.weixin.qq.com/s/ahpV7Poj5wHmtUP6vqy3gg)\n\n[这是我的博客地址](http://zkqiang.cn)\n\n[通过引号设置引用名](http://prod.zkqiang.cn/wxeditor \"这是自定义的引用名\")\n\n[本项目是 Fork 自 Lyric 原项目后的二次开发，感谢他的贡献！](https://github.com/lyricat/wechat-format \"原项目代码库\")\n"
  },
  {
    "path": "src/assets/scripts/editor.js",
    "content": "let app = new Vue({\n  el: '#app',\n  data: function () {\n    let d = {\n      aboutOutput: '',\n      output: '',\n      source: '',\n      editorThemes: [\n        { label: 'base16-light', value: 'base16-light' },\n        { label: 'duotone-light', value: 'duotone-light' },\n        { label: 'monokai', value: 'monokai' }\n      ],\n      editor: null,\n      builtinFonts: [\n        {\n          label: '无衬线',\n          value: \"-apple-system-font,BlinkMacSystemFont, Helvetica Neue, PingFang SC, Hiragino Sans GB , Microsoft YaHei UI , Microsoft YaHei ,Arial,sans-serif\"\n        },\n        {\n          label: '衬线',\n          value: \"Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif\"\n        }\n      ],\n      sizeOption: [\n        { label: '14px', value: '14px', desc: '稍小' },\n        { label: '15px', value: '15px', desc: '默认' },\n        { label: '16px', value: '16px', desc: '稍大' },\n        { label: '17px', value: '17px', desc: '很大' },\n      ],\n      themeOption: [\n        { label: 'default', value: 'default', author: '张凯强' },\n        { label: 'lyric', value: 'lyric', author: 'Lyric' },\n        { label: 'lupeng', value: 'lupeng', author: '鲁鹏' }\n      ],\n      styleThemes: {\n        default: defaultTheme,\n        lyric: lyricTheme,\n        lupeng: lupengTheme\n      },\n      aboutDialogVisible: false\n    };\n    d.currentEditorTheme = d.editorThemes[0].value;\n    d.currentFont = d.builtinFonts[0].value;\n    d.currentSize = d.sizeOption[1].value;\n    d.currentTheme = d.themeOption[0].value;\n    return d;\n  },\n  mounted() {\n    let self = this;\n    this.editor = CodeMirror.fromTextArea(\n      document.getElementById('editor'),\n      {\n        lineNumbers: false,\n        lineWrapping: true,\n        styleActiveLine: true,\n        theme: this.currentEditorTheme,\n        mode: 'text/x-markdown',\n      }\n    );\n    this.editor.on(\"change\", function (cm, change) {\n      self.refresh();\n      self.saveEditorContent();\n    });\n    this.wxRenderer = new WxRenderer({\n      theme: this.styleThemes.default,\n      fonts: this.currentFont,\n      size: this.currentSize\n    });\n    // 如果有编辑内容被保存则读取，否则加载默认文档\n    if (localStorage.getItem(\"__editor_content\")) {\n      this.editor.setValue(localStorage.getItem(\"__editor_content\"));\n    } else {\n      axios({\n        method: 'get',\n        url: './assets/default-content.md',\n      }).then(function (resp) {\n        self.editor.setValue(resp.data)\n      })\n    }\n  },\n  methods: {\n    renderWeChat: function (source) {\n      let output = marked(source, { renderer: this.wxRenderer.getRenderer() });\n      if (this.wxRenderer.hasFootnotes()) {\n        // 去除第一行的 margin-top\n        output = output.replace(/(style=\".*?)\"/, '$1;margin-top: 0\"');\n        // 引用注脚\n        output += this.wxRenderer.buildFootnotes();\n        // 附加的一些 style\n        output += this.wxRenderer.buildAddition();\n      }\n      return output\n    },\n    editorThemeChanged: function (editorTheme) {\n      this.editor.setOption('theme', editorTheme)\n    },\n    fontChanged: function (fonts) {\n      this.wxRenderer.setOptions({\n        fonts: fonts\n      });\n      this.refresh()\n    },\n    sizeChanged: function (size) {\n      this.wxRenderer.setOptions({\n        size: size\n      });\n      this.refresh()\n    },\n    themeChanged: function (themeName) {\n      let themeObject = this.styleThemes[themeName];\n      this.wxRenderer.setOptions({\n        theme: themeObject\n      });\n      this.refresh()\n    },\n    // 刷新右侧预览\n    refresh: function () {\n      this.output = this.renderWeChat(this.editor.getValue(0))\n    },\n    // 将左侧编辑器内容保存到 LocalStorage\n    saveEditorContent: function () {\n      let content = this.editor.getValue(0);\n      if (content){\n        localStorage.setItem(\"__editor_content\", content);\n      } else {\n        localStorage.removeItem(\"__editor_content\");\n      }\n    },\n    copy: function () {\n      let clipboardDiv = document.getElementById('output');\n      clipboardDiv.focus();\n      window.getSelection().removeAllRanges();\n      let range = document.createRange();\n      range.setStartBefore(clipboardDiv.firstChild);\n      range.setEndAfter(clipboardDiv.lastChild);\n      window.getSelection().addRange(range);\n\n      try {\n        if (document.execCommand('copy')) {\n          this.$message({\n            message: '已复制到剪贴板', type: 'success'\n          })\n        } else {\n          this.$message({\n            message: '未能复制到剪贴板，请全选后右键复制', type: 'warning'\n          })\n        }\n      } catch (err) {\n        this.$message({\n          message: '未能复制到剪贴板，请全选后右键复制', type: 'warning'\n        })\n      }\n    },\n    openWindow: function (url) {\n      window.open(url);\n    }\n  },\n  updated: function () {\n    this.$nextTick(function () {\n      prettyPrint()\n    })\n  }\n});\n"
  },
  {
    "path": "src/assets/scripts/loading.js",
    "content": "// 加载完成隐藏 loading 界面\nwindow.onload = () => {\n  $('#loading').hide();\n};\n"
  },
  {
    "path": "src/assets/scripts/renderers/wx-renderer.js",
    "content": "let WxRenderer = function (opts) {\n  this.opts = opts;\n  let ENV_USE_REFERENCES = true;\n  let ENV_STRETCH_IMAGE = true;\n\n  let footnotes = [];\n  let footnoteIndex = 0;\n  let styleMapping = null;\n\n  let CODE_FONT_FAMILY = \"Menlo, Operator Mono, Consolas, Monaco, monospace\";\n\n  let merge = function (base, extend) {\n    return Object.assign({}, base, extend)\n  };\n\n  this.buildTheme = function (themeTpl) {\n    let mapping = {};\n    let base = merge(themeTpl.BASE, {\n      'font-family': this.opts.fonts,\n      'font-size': this.opts.size\n    });\n    let base_block = merge(base, {});\n    for (let ele in themeTpl.inline) {\n      if (themeTpl.inline.hasOwnProperty(ele)) {\n        let style = themeTpl.inline[ele];\n        if (ele === 'codespan') {\n          style['font-family'] = CODE_FONT_FAMILY;\n          style['white-space'] = 'normal';\n        }\n        mapping[ele] = merge(base, style)\n      }\n    }\n    for (let ele in themeTpl.block) {\n      if (themeTpl.block.hasOwnProperty(ele)) {\n        let style = themeTpl.block[ele];\n        if (ele === 'code') {\n          style['font-family'] = CODE_FONT_FAMILY\n        }\n        mapping[ele] = merge(base_block, style)\n      }\n    }\n    return mapping\n  };\n\n  let getStyles = function (tokenName, addition) {\n    let arr = [];\n    let dict = styleMapping[tokenName];\n    if (!dict) return '';\n    for (const key in dict) {\n      arr.push(key + ':' + dict[key])\n    }\n    return `style=\"${ arr.join(';') + (addition || '') }\"`\n  };\n\n  let addFootnote = function (title, link) {\n    footnoteIndex += 1;\n    footnotes.push([footnoteIndex, title, link]);\n    return footnoteIndex\n  };\n\n  this.buildFootnotes = function () {\n    let footnoteArray = footnotes.map(function (x) {\n      if (x[1] === x[2]) {\n        return `<code style=\"font-size: 90%; opacity: 0.6;\">[${ x[0] }]</code>: <i>${ x[1] }</i><br/>`\n      }\n      return `<code style=\"font-size: 90%; opacity: 0.6;\">[${ x[0] }]</code> ${ x[1] }: <i>${ x[2] }</i><br/>`\n    });\n    return `<h3 ${ getStyles('h3') }>References</h3><p ${ getStyles('footnotes') }>${ footnoteArray.join('\\n') }</p>`\n  };\n\n  this.buildAddition = function () {\n    return '<style>.preview-wrapper pre::before{' +\n      'font-family:\"SourceSansPro\",\"HelveticaNeue\",Arial,sans-serif;' +\n      'position:absolute;' +\n      'top:0;' +\n      'right:0;' +\n      'color:#ccc;' +\n      'text-align:right;' +\n      'font-size:0.8em;' +\n      'padding:5px10px0;' +\n      'line-height:15px;' +\n      'height:15px;' +\n      'font-weight:600;' +\n      '}</style>'\n  };\n\n  this.setOptions = function (newOpts) {\n    this.opts = merge(this.opts, newOpts)\n  };\n\n  this.hasFootnotes = function () {\n    return footnotes.length !== 0\n  };\n\n  this.getRenderer = function () {\n    footnotes = [];\n    footnoteIndex = 0;\n\n    styleMapping = this.buildTheme(this.opts.theme);\n    let renderer = new marked.Renderer();\n    FuriganaMD.register(renderer);\n\n    renderer.heading = function (text, level) {\n      switch (level) {\n        case 1:\n          return `<h1 ${ getStyles('h1') }>${ text }</h1>`;\n        case 2:\n          return `<h2 ${ getStyles('h2') }>${ text }</h2>`;\n        case 3:\n          return `<h3 ${ getStyles('h3') }>${ text }</h3>`;\n        default:\n          return `<h4 ${ getStyles('h4') }>${ text }</h4>`;\n      }\n    };\n    renderer.paragraph = function (text) {\n      return `<p ${ getStyles('p') }>${ text }</p>`\n    };\n    renderer.blockquote = function (text) {\n      text = text.replace(/<p.*?>/, `<p ${ getStyles('blockquote_p') }>`);\n      return `<blockquote ${ getStyles('blockquote') }>${ text }</blockquote>`\n    };\n    renderer.code = function (text, infoString) {\n      text = text.replace(/</g, \"&lt;\");\n      text = text.replace(/>/g, \"&gt;\");\n\n      let lines = text.split('\\n');\n      let codeLines = [];\n      let numbers = [];\n      for (let i = 0; i < lines.length; i++) {\n        const line = lines[i];\n        codeLines.push(`<code class=\"prettyprint\"><span class=\"code-snippet_outer\">${ (line || '<br>') }</span></code>`);\n        numbers.push('<li></li>')\n      }\n      let lang = infoString || '';\n      return `<section class=\"code-snippet__fix code-snippet__js\">`\n        + `<ul class=\"code-snippet__line-index code-snippet__js\">${ numbers.join('') }</ul>`\n        + `<pre class=\"code-snippet__js\" data-lang=\"${ lang }\">`\n        + codeLines.join('')\n        + `</pre></section>`\n    };\n    renderer.codespan = function (text, infoString) {\n      return `<code ${ getStyles('codespan') }>${ text }</code>`\n    };\n    renderer.listitem = function (text) {\n      return `<span ${ getStyles('listitem') }><span style=\"margin-right: 10px;\"><%s/></span>${ text }</span>`;\n    };\n    renderer.list = function (text, ordered, start) {\n      text = text.replace(/<\\/*p.*?>/g, '');\n      let segments = text.split(`<%s/>`);\n      if (!ordered) {\n        text = segments.join('•');\n        return `<p ${ getStyles('ul') }>${ text }</p>`;\n      }\n      text = segments[0];\n      for (let i = 1; i < segments.length; i++) {\n        text = text + i + '.' + segments[i];\n      }\n      return `<p ${ getStyles('ol') }>${ text }</p>`;\n    };\n    renderer.image = function (href, title, text) {\n      let subText = '';\n      if (text) {\n        subText = `<figcaption ${ getStyles('figcaption') }>${ text }</figcaption>`\n      }\n      let figureStyles = getStyles('figure');\n      let imgStyles = getStyles(ENV_STRETCH_IMAGE ? 'image' : 'image_org');\n      return `<figure ${ figureStyles }><img ${ imgStyles } src=\"${ href }\" title=\"${ title }\" alt=\"${ text }\"/>${ subText }</figure>`\n    };\n    renderer.link = function (href, title, text) {\n      if (href.indexOf('https://mp.weixin.qq.com') === 0) {\n        return `<a href=\"${ href }\" title=\"${ (title || text) }\" ${ getStyles('wx_link') }>${ text }</a>`;\n      } else if (href === text) {\n        return text;\n      } else {\n        if (ENV_USE_REFERENCES) {\n          let ref = addFootnote(title || text, href);\n          return `<span ${ getStyles('link') }>${ text }<sup>[${ ref }]</sup></span>`;\n        } else {\n          return `<a href=\"${ href }\" title=\"${ (title || text) }\" ${ getStyles('link') }>${ text }</a>`;\n        }\n      }\n    };\n    renderer.strong = function (text) {\n      return `<strong ${ getStyles('strong') }>${ text }</strong>`;\n    };\n    renderer.em = function (text) {\n      return `<p ${ getStyles('p', ';font-style: italic;')}>${ text }</p>`\n    };\n      renderer.table = function (header, body) {\n      return `<table class=\"preview-table\"><thead ${ getStyles('thead') }>${ header }</thead><tbody>${ body }</tbody></table>`;\n    };\n    renderer.tablecell = function (text, flags) {\n      return `<td ${ getStyles('td') }>${ text }</td>`;\n    };\n    renderer.hr = function () {\n      return `<hr style=\"border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);\">`;\n    };\n    return renderer\n  }\n};\n"
  },
  {
    "path": "src/assets/scripts/themes/default.js",
    "content": "let defaultTheme = {\n  BASE: {\n    'text-align': 'left',\n    'color': '#3f3f3f',\n    'line-height': '1.75',\n  },\n  BASE_BLOCK: {\n    'margin': '1em 8px'\n  },\n  // block element\n  block: {\n    h1: {\n      'font-size': '1.2em',\n      'text-align': 'center',\n      'font-weight': 'bold',\n      'display': 'table',\n      'margin': '2em auto 1em auto',\n      'padding': '0 1em',\n      'border-bottom': '1px solid rgb(248,57,41)'\n    },\n    h2: {\n      'font-size': '1.2em',\n      'text-align': 'center',\n      'font-weight': 'bold',\n      'display': 'table',\n      'margin': '4em auto 2em auto',\n      'padding': '0 1em',\n      'border-bottom': '1px solid rgb(248,57,41)'\n    },\n    h3: {\n      'font-weight': 'bold',\n      'font-size': '1.1em',\n      'margin': '2em 8px 0.75em 0',\n      'padding-bottom': '.1em',\n      // 'border-bottom': '1px solid #eaecef',\n      'padding-left': '8px',\n      'border-left': '4px solid rgb(248,57,41)'\n    },\n    h4: {\n      'font-weight': 'bold',\n      'font-size': '1em',\n      'margin': '2em 8px 0.5em 8px',\n    },\n    p: {\n      'margin': '1.5em 8px',\n      'letter-spacing': '0.1em'\n    },\n    blockquote: {\n      'font-style': 'normal',\n      'border-left': 'none',\n      'padding': '1em',\n      'border-radius': '4px',\n      'color': '#FEEEED',\n      'background': 'rgba(27,31,35,.05)',\n      'margin': '2em 8px'\n    },\n    blockquote_p: {\n      'letter-spacing': '0.1em',\n      'color': 'rgb(80, 80, 80)',\n      'font-family': 'PingFangSC-light, PingFangTC-light, Open Sans, Helvetica Neue, sans-serif',\n      'font-size': '1em',\n      'display': 'inline',\n    },\n    code: {\n      'font-size': '80%',\n      'overflow': 'auto',\n      'color': '#333',\n      'background': 'rgb(247, 247, 247)',\n      'border-radius': '2px',\n      'padding': '10px',\n      'line-height': '1.5',\n      'border': '1px solid rgb(236,236,236)',\n      'margin': '20px 0',\n    },\n    image: {\n      'border-radius': '4px',\n      'display': 'block',\n      'margin': '0.5em auto',\n      'width': '100%'\n    },\n    image_org: {\n      'border-radius': '4px',\n      'display': 'block'\n    },\n    ol: {\n      'margin-left': '0',\n      'padding-left': '1em'\n    },\n    ul: {\n      'margin-left': '0',\n      'padding-left': '1em',\n      'list-style': 'circle'\n    },\n    footnotes: {\n      'margin': '0.5em 8px',\n      'font-size': '80%'\n    },\n    figure: {\n      'margin': '1.5em 8px',\n    }\n  },\n  inline: {\n    // inline element\n    listitem: {\n      'text-indent': '-1em',\n      'display': 'block',\n      'margin': '0.5em 8px'\n    },\n    codespan: {\n      'font-size': '90%',\n      'color': '#d14',\n      'background': 'rgba(27,31,35,.05)',\n      'padding': '3px 5px',\n      'border-radius': '4px',\n    },\n    link: {\n      'color': '#009926'\n    },\n    wx_link: {\n      'color': '#0080ff',\n      'text-decoration': 'none',\n      'border-bottom': '1px solid #d1e9ff'\n    },\n    strong: {\n      'color': '#ff5f2e',\n      'font-weight': 'bold',\n    },\n    table: {\n      'border-collapse': 'collapse',\n      'text-align': 'center',\n      'margin': '1em 8px'\n    },\n    thead: {\n      'background': 'rgba(0, 0, 0, 0.05)'\n    },\n    td: {\n      'font-size': '80%',\n      'border': '1px solid #dfdfdf',\n      'padding': '0.25em 0.5em'\n    },\n    footnote: {\n      'font-size': '12px'\n    },\n    figcaption: {\n      'text-align': 'center',\n      'color': '#888',\n      'font-size': '0.8em'\n    }\n  }\n};\n"
  },
  {
    "path": "src/assets/scripts/themes/lupeng.js",
    "content": "let lupengTheme = {\n  BASE: {\n    'text-align': 'left',\n    'color': '#595959',\n    'line-height': '1.55em',\n    'letter-spacing': '0.06em'\n  },\n  BASE_BLOCK: {\n    'margin': '20px 10px'\n  },\n  block: {\n    h1: {\n      'font-size': '140%',\n      'text-align': 'center',\n      'font-weight': 'normal',\n      'margin': '80px 10px 40px 10px'\n    },\n    h2: {\n      'font-size': '140%',\n      'text-align': 'center',\n      'font-weight': 'normal',\n      'margin': '80px 10px 40px 10px'\n    },\n    h3: {\n      'font-weight': 'bold',\n      'font-size': '120%',\n      'margin': '40px 10px 20px 10px'\n    },\n    h4: {\n      'font-weight': 'bold',\n      'font-size': '100%',\n      'margin': '20px 10px 10px 10px'\n    },\n    p: {\n      'margin': '10px 10px',\n      'line-height': '1.6'\n    },\n    blockquote: {\n      'color': '#9a9a9a',\n      'padding-left': '10px',\n      // 'padding-top': '0.05px',\n      'background-color': '#fefefe',\n      'line-height': '1.6',\n      'border-left': '3px solid #dbdbdb',\n      'font-size': '15px',\n      'margin': '1em 0'\n    },\n    code: {\n      'font-size': '80%',\n      'overflow': 'auto',\n      'color': '#333',\n      'background': 'rgb(247, 247, 247)',\n      'border-radius': '2px',\n      'padding': '10px',\n      'line-height': '1.3',\n      'border': '1px solid rgb(236,236,236)',\n      'margin': '20px 0',\n    },\n    image: {\n      'border-radius': '4px',\n      'display': 'block',\n      'margin': '20px auto',\n      'width': '100%',\n    },\n    image_org: {\n      'border-radius': '4px',\n      'display': 'block',\n    },\n    ol: {\n      'margin-left': '0',\n      'padding-left': '20px'\n    },\n    ul: {\n      'margin-left': '0',\n      'padding-left': '20px',\n      'list-style': 'circle',\n    },\n    footnotes: {\n      'margin': '10px 10px',\n      'font-size': '14px'\n    }\n  },\n  inline: {\n    // inline element\n    listitem: {\n      'text-indent': '-20px',\n      'display': 'block',\n      'margin': '10px 10px',\n    },\n    codespan: {\n      'font-size': '0.8em',\n      'color': '#d14',\n      'background': '#fefefe',\n      'padding': '3px 5px 0px',\n      'margin': '0px 2px',\n      'border': '1px solid #ddd',\n      'border-radius': '3px',\n    },\n    link: {\n      'color': '#ff3502'\n    },\n    wx_link: {\n      'color': '#576b95',\n      'text-decoration': 'none'\n    },\n    strong: {\n      'font-weight': 'bold',\n    },\n    table: {\n      'border-collapse': 'collapse',\n      'margin': '20px 0',\n    },\n    thead: {\n      'background': 'rgba(0,0,0,0.05)',\n    },\n    td: {\n      'font-size': '80%',\n      'border': '1px solid #dfdfdf',\n      'padding': '4px 8px',\n    },\n    footnote: {\n      'font-size': '12px',\n    }\n  }\n};\n"
  },
  {
    "path": "src/assets/scripts/themes/lyric.js",
    "content": "let lyricTheme = {\n  BASE: {\n    'text-align': 'left',\n    'color': '#3f3f3f',\n    'line-height': '1.5'\n  },\n  BASE_BLOCK: {\n    'margin': '20px 10px'\n  },\n  // block element\n  block: {\n    h1: {\n      'font-size': '140%',\n      'text-align': 'center',\n      'font-weight': 'normal',\n      'margin': '80px 10px 40px 10px'\n    },\n    h2: {\n      'font-size': '140%',\n      'text-align': 'center',\n      'font-weight': 'normal',\n      'margin': '80px 10px 40px 10px'\n    },\n    h3: {\n      'font-weight': 'bold',\n      'font-size': '120%',\n      'margin': '40px 10px 20px 10px'\n    },\n    h4: {\n      'font-weight': 'bold',\n      'font-size': '100%',\n      'margin': '20px 10px 10px 10px'\n    },\n    p: {\n      'margin': '10px 10px',\n      'line-height': '1.6'\n    },\n    blockquote: {\n      'color': 'rgb(91, 91, 91)',\n      'padding': '1px 0 1px 10px',\n      'background': 'rgba(158, 158, 158, 0.1)',\n      'border-left': '3px solid rgb(158,158,158)',\n    },\n    code: {\n      'font-size': '80%',\n      'overflow': 'auto',\n      'color': '#333',\n      'background': 'rgb(247, 247, 247)',\n      'border-radius': '2px',\n      'padding': '10px',\n      'line-height': '1.3',\n      'border': '1px solid rgb(236,236,236)',\n      'margin': '20px 0',\n    },\n    image: {\n      'border-radius': '4px',\n      'display': 'block',\n      'margin': '20px auto',\n      'width': '100%',\n    },\n    image_org: {\n      'border-radius': '4px',\n      'display': 'block',\n    },\n    ol: {\n      'margin-left': '0',\n      'padding-left': '20px'\n    },\n    ul: {\n      'margin-left': '0',\n      'padding-left': '20px',\n      'list-style': 'circle',\n    },\n    footnotes: {\n      'margin': '10px 10px',\n      'font-size': '14px'\n    }\n  },\n  inline: {\n    // inline element\n    listitem: {\n      'text-indent': '-20px',\n      'display': 'block',\n      'margin': '10px 10px',\n    },\n    codespan: {\n      'font-size': '90%',\n      // 'font-family': FONT_FAMILY_MONO,\n      'color': '#ff3502',\n      'background': '#f8f5ec',\n      'padding': '3px 5px',\n      'border-radius': '2px',\n    },\n    link: {\n      'color': '#ff3502'\n    },\n    strong: {\n      'color': '#ff3502'\n    },\n    table: {\n      'border-collapse': 'collapse',\n      'margin': '20px 0',\n    },\n    thead: {\n      'background': 'rgba(0,0,0,0.05)',\n    },\n    td: {\n      'font-size': '80%',\n      'border': '1px solid #dfdfdf',\n      'padding': '4px 8px',\n    },\n    footnote: {\n      'font-size': '12px',\n    }\n  }\n};\n"
  },
  {
    "path": "src/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">\n  <title>微信公众号 Markdown 编辑器</title>\n  <link rel=\"shortcut icon\" href=\"assets/images/favicon.png\">\n  <link rel=\"apple-touch-icon-precomposed\" href=\"assets/images/favicon.png\">\n\n  <link rel=\"stylesheet\" href=\"assets/css/loading.css\">\n\n  <link rel=\"stylesheet\" href=\"https://cdn.staticfile.org/element-ui/2.11.1/theme-chalk/index.css\">\n  <link rel=\"stylesheet\" href=\"https://cdn.staticfile.org/codemirror/5.48.4/codemirror.min.css\">\n  <link rel=\"stylesheet\" href=\"https://cdn.staticfile.org/codemirror/5.48.4/theme/base16-light.min.css\">\n  <link rel=\"stylesheet\" href=\"https://cdn.staticfile.org/codemirror/5.48.4/theme/duotone-light.min.css\">\n  <link rel=\"stylesheet\" href=\"https://cdn.staticfile.org/codemirror/5.48.4/theme/monokai.min.css\">\n\n  <link rel=\"stylesheet\" href=\"libs/prettify/color-themes/github-v2.min.css\">\n\n  <link rel=\"stylesheet\" href=\"assets/css/app.css\">\n</head>\n<body>\n  <!--loading 界面-->\n  <div class=\"loading\" id=\"loading\">\n    <div class=\"loading-wrapper\">\n      <div class=\"loading-text\">Loading...</div>\n      <div class=\"loading-anim\"></div>\n    </div>\n  </div>\n\n  <!--应用主体-->\n  <div id=\"app\" class=\"container\">\n    <el-container>\n      <el-header class=\"top\">\n        <div><img src=\"assets/images/favicon.png\" class=\"web-icon\" alt=\"icon\"> <span\n            class=\"web-title\">公众号 Markdown 编辑器 </span></div>\n        <el-form size=\"mini\" class=\"ctrl\" :inline=true>\n          <el-form-item label=\"Editor Themes\">\n            <el-select v-model=\"currentEditorTheme\" size=\"mini\" placeholder=\"选择字体\" @change=\"editorThemeChanged\">\n              <el-option v-for=\"editorTheme in editorThemes\" :key=\"editorTheme.value\" :label=\"editorTheme.label\"\n                         :value=\"editorTheme.value\">\n              </el-option>\n            </el-select>\n          </el-form-item>\n          <el-form-item label=\"Fonts\">\n            <el-select v-model=\"currentFont\" size=\"mini\" placeholder=\"选择字体\" @change=\"fontChanged\">\n              <el-option v-for=\"font in builtinFonts\" :style=\"{fontFamily: font.value}\"\n                         :key=\"font.value\"\n                         :label=\"font.label\"\n                         :value=\"font.value\">\n                <span class=\"select-item-left\">{{ font.label }}</span>\n                <span class=\"select-item-right\">Abc</span>\n              </el-option>\n            </el-select>\n          </el-form-item>\n          <el-form-item label=\"Font Size\">\n            <el-select v-model=\"currentSize\" size=\"mini\" placeholder=\"选择段落字体大小\" @change=\"sizeChanged\">\n              <el-option v-for=\"size in sizeOption\"\n                         :key=\"size.value\"\n                         :label=\"size.label\"\n                         :value=\"size.value\">\n                <span class=\"select-item-left\">{{ size.label }}</span>\n                <span class=\"select-item-right\">{{ size.desc }}</span>\n              </el-option>\n            </el-select>\n          </el-form-item>\n          <el-form-item label=\"Themes\">\n            <el-select v-model=\"currentTheme\" size=\"mini\" placeholder=\"选择主题样式\" @change=\"themeChanged\">\n              <el-option v-for=\"theme in themeOption\" :key=\"theme.value\" :label=\"theme.label\" :value=\"theme.value\">\n                <span class=\"select-item-left\">{{ theme.label }}</span>\n                <span class=\"select-item-right\">{{ theme.author }}</span>\n              </el-option>\n            </el-select>\n          </el-form-item>\n        </el-form>\n        <el-button class=\"about\" @click=\"aboutDialogVisible = true\">关于</el-button>\n      </el-header>\n      <el-main class=\"main-body\">\n        <el-row :gutter=\"10\" class=\"main-section\">\n          <el-col :span=\"12\">\n              <textarea\n                  id=\"editor\"\n                  type=\"textarea\"\n                  placeholder=\"Your markdown here.\"\n                  v-model=\"source\">\n              </textarea>\n          </el-col>\n          <el-col :span=\"12\" class=\"preview-wrapper\" id=\"preview\">\n            <section>\n              <div class=\"hint\">全选复制或<a href=\"#\" @click=\"copy\" class=\"copy-button\">点此复制</a>，然后在公众号编辑器粘贴</div>\n              <div class=\"preview\" contenteditable=\"true\">\n                <div id=\"output\" v-html=\"output\">\n                </div>\n              </div>\n            </section>\n          </el-col>\n        </el-row>\n      </el-main>\n    </el-container>\n\n    <el-dialog title=\"关于\" :visible.sync=\"aboutDialogVisible\" width=\"30%\" center>\n      <div>\n        <p>一款可以将 Markdown 转换为微信公众号文章的在线编辑器，</p>\n        <p>这让你在公众号创作时，摆脱繁琐地排版样式，</p>\n        <p>可以把更多的时间专注于文章本身。</p>\n        <p>除了常规 Markdown 格式化，还增加了外链引用、注音样式等。</p>\n      </div>\n      <div style=\"text-align: center;\">\n        <img src=\"https://static.zkqiang.cn/images/20191019181436.JPG-slim\" style=\"max-width: 300px\">\n      </div>\n      <span slot=\"footer\" class=\"dialog-footer\">\n          <el-button type=\"primary\"\n                     @click=\"openWindow('https://github.com/zkqiang/wechat-format')\">查看 GitHub 仓库</el-button>\n        </span>\n    </el-dialog>\n  </div>\n\n  <script src=\"https://cdn.staticfile.org/vue/2.6.10/vue.min.js\"></script>\n  <script src=\"https://cdn.staticfile.org/axios/0.19.0-beta.1/axios.min.js\"></script>\n  <script src=\"https://cdn.staticfile.org/marked/0.7.0/marked.min.js\"></script>\n  <script src=\"https://cdn.staticfile.org/codemirror/5.48.4/codemirror.min.js\"></script>\n  <script src=\"https://cdn.staticfile.org/codemirror/5.48.4/mode/markdown/markdown.min.js\"></script>\n  <script src=\"https://cdn.staticfile.org/prettify/r298/prettify.min.js\"></script>\n  <script src=\"https://cdn.staticfile.org/element-ui/2.11.1/index.js\"></script>\n  <script src=\"https://cdn.staticfile.org/jquery/3.4.1/jquery.min.js\"></script>\n\n  <script async src=\"//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js\"></script>\n\n  <script src=\"libs/sync-scroll.js\"></script>\n  <script src=\"libs/FuriganaMD.js\"></script>\n\n  <script src=\"assets/scripts/themes/default.js\"></script>\n  <script src=\"assets/scripts/themes/lyric.js\"></script>\n  <script src=\"assets/scripts/themes/lupeng.js\"></script>\n  <script src=\"assets/scripts/renderers/wx-renderer.js\"></script>\n  <script src=\"assets/scripts/editor.js\"></script>\n  <script src=\"assets/scripts/loading.js\"></script>\n\n</body>\n</html>\n"
  },
  {
    "path": "src/libs/FuriganaMD.js",
    "content": "// 注音功能来自于\n// https://github.com/amclees/furigana-markdown\n// 详见上述文档\n\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n    typeof define === 'function' && define.amd ? define(factory) :\n      (global.FuriganaMD = factory());\n}(this, (function () {\n  'use strict';\n\n// This function escapes special characters for use in a regex constructor.\n  function escapeForRegex(string) {\n    return string.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&');\n  }\n\n  function emptyStringFilter(block) {\n    return block !== '';\n  }\n\n  const kanjiRange = '\\\\u4e00-\\\\u9faf';\n  const kanjiBlockRegex = new RegExp(`[${kanjiRange}]+`, 'g');\n  const nonKanjiBlockRegex = new RegExp(`[^${kanjiRange}]+`, 'g');\n  const kanaWithAnnotations = '\\\\u3041-\\\\u3095\\\\u3099-\\\\u309c\\\\u3081-\\\\u30fa\\\\u30fc';\n  const furiganaSeperators = '.．。・';\n  const seperatorRegex = new RegExp(`[${furiganaSeperators}]`, 'g');\n\n  const singleKanjiRegex = new RegExp(`^[${kanjiRange}]$`);\n\n  function isKanji(character) {\n    return character.match(singleKanjiRegex);\n  }\n\n  const innerRegexString = '(?:[^\\\\u0000-\\\\u007F]|\\\\w)+';\n\n  let regexList = [];\n  let previousFuriganaForms = '';\n\n  function updateRegexList(furiganaForms) {\n    previousFuriganaForms = furiganaForms;\n    let formArray = furiganaForms.split('|');\n    if (formArray.length === 0) {\n      formArray = ['[]:^:()'];\n    }\n    regexList = formArray.map(form => {\n      let furiganaComponents = form.split(':');\n      if (furiganaComponents.length !== 3) {\n        furiganaComponents = ['[]', '^', '()'];\n      }\n      const mainBrackets = furiganaComponents[0];\n      const seperator = furiganaComponents[1];\n      const furiganaBrackets = furiganaComponents[2];\n      return new RegExp(\n        escapeForRegex(mainBrackets[0]) +\n        '(' + innerRegexString + ')' +\n        escapeForRegex(mainBrackets[1]) +\n        escapeForRegex(seperator) +\n        escapeForRegex(furiganaBrackets[0]) +\n        '(' + innerRegexString + ')' +\n        escapeForRegex(furiganaBrackets[1]),\n        'g'\n      );\n    });\n  }\n\n  let autoRegexList = [];\n  let previousAutoBracketSets = '';\n\n  function updateAutoRegexList(autoBracketSets) {\n    previousAutoBracketSets = autoBracketSets;\n    autoRegexList = autoBracketSets.split('|').map(brackets => {\n      /*\n        Sample built regex:\n        /(^|[^\\u4e00-\\u9faf]|)([\\u4e00-\\u9faf]+)([\\u3041-\\u3095\\u3099-\\u309c\\u3081-\\u30fa\\u30fc]*)【((?:[^【】\\u4e00-\\u9faf]|w)+)】/g\n      */\n      return new RegExp(\n        `(^|[^${kanjiRange}]|)` +\n        `([${kanjiRange}]+)` +\n        `([${kanaWithAnnotations}]*)` +\n        escapeForRegex(brackets[0]) +\n        `((?:[^${escapeForRegex(brackets)}\\\\u0000-\\\\u007F]|\\\\w|[${furiganaSeperators}])+)` +\n        escapeForRegex(brackets[1]),\n        'g'\n      );\n    });\n  }\n\n  let replacementTemplate = '';\n  let replacementBrackets = '';\n\n  function updateReplacementTemplate(furiganaFallbackBrackets) {\n    if (furiganaFallbackBrackets.length !== 2) {\n      furiganaFallbackBrackets = '【】';\n    }\n    replacementBrackets = furiganaFallbackBrackets;\n    replacementTemplate = `<ruby>$1<rp>${furiganaFallbackBrackets[0]}</rp><rt style=\"line-height:1;font-size:10px;\">$2</rt><rp>${furiganaFallbackBrackets[1]}</rp></ruby>`;\n  }\n\n  updateReplacementTemplate('【】');\n\n  function addFurigana(text, options) {\n    if (options.furiganaForms !== previousFuriganaForms) {\n      updateRegexList(options.furiganaForms);\n    }\n    if (options.furiganaFallbackBrackets !== replacementBrackets) {\n      updateReplacementTemplate(options.furiganaFallbackBrackets);\n    }\n    regexList.forEach(regex => {\n      text = text.replace(regex, (match, wordText, furiganaText, offset, mainText) => {\n        if (match.indexOf('\\\\') === -1 && mainText[offset - 1] !== '\\\\') {\n          if ((!options.furiganaPatternMatching) || wordText.search(kanjiBlockRegex) === -1 || wordText[0].search(kanjiBlockRegex) === -1) {\n            return replacementTemplate.replace('$1', wordText).replace('$2', furiganaText);\n          } else {\n            let originalFuriganaText = (' ' + furiganaText).slice(1);\n            let nonKanji = wordText.split(kanjiBlockRegex).filter(emptyStringFilter);\n            let kanji = wordText.split(nonKanjiBlockRegex).filter(emptyStringFilter);\n            let replacementText = '';\n            let lastUsedKanjiIndex = 0;\n            if (nonKanji.length === 0) {\n              return replacementTemplate.replace('$1', wordText).replace('$2', furiganaText);\n            }\n\n            nonKanji.forEach((currentNonKanji, index) => {\n              if (furiganaText === undefined) {\n                if (index < kanji.length) {\n                  replacementText += kanji[index];\n                }\n\n                replacementText += currentNonKanji;\n                return;\n              }\n              let splitFurigana = furiganaText.split(new RegExp(escapeForRegex(currentNonKanji) + '(.*)')).filter(emptyStringFilter);\n\n              lastUsedKanjiIndex = index;\n              replacementText += replacementTemplate.replace('$1', kanji[index]).replace('$2', splitFurigana[0]);\n              replacementText += currentNonKanji;\n\n              furiganaText = splitFurigana[1];\n            });\n            if (furiganaText !== undefined && lastUsedKanjiIndex + 1 < kanji.length) {\n              replacementText += replacementTemplate.replace('$1', kanji[lastUsedKanjiIndex + 1]).replace('$2', furiganaText);\n            } else if (furiganaText !== undefined) {\n              return replacementTemplate.replace('$1', wordText).replace('$2', originalFuriganaText);\n            } else if (lastUsedKanjiIndex + 1 < kanji.length) {\n              replacementText += kanji[lastUsedKanjiIndex + 1];\n            }\n            return replacementText;\n          }\n        } else {\n          return match;\n        }\n      });\n    });\n\n    if (!options.furiganaStrictMode) {\n      if (options.furiganaAutoBracketSets !== previousAutoBracketSets) {\n        updateAutoRegexList(options.furiganaAutoBracketSets);\n      }\n      autoRegexList.forEach(regex => {\n        text = text.replace(regex, (match, preWordTerminator, wordKanji, wordKanaSuffix, furiganaText, offset, mainText) => {\n          if (match.indexOf('\\\\') === -1) {\n            if (options.furiganaPatternMatching) {\n              let rubies = [];\n\n              let furigana = furiganaText;\n\n              let stem = (' ' + wordKanaSuffix).slice(1);\n              for (let i = furiganaText.length - 1; i >= 0; i--) {\n                if (wordKanaSuffix.length === 0) {\n                  furigana = furiganaText.substring(0, i + 1);\n                  break;\n                }\n                if (furiganaText[i] !== wordKanaSuffix.slice(-1)) {\n                  furigana = furiganaText.substring(0, i + 1);\n                  break;\n                }\n                wordKanaSuffix = wordKanaSuffix.slice(0, -1);\n              }\n\n              if (furiganaSeperators.split('').reduce(\n                (noSeperator, seperator) => {\n                  return noSeperator && (furigana.indexOf(seperator) === -1);\n                },\n                true\n              )) {\n                rubies = [replacementTemplate.replace('$1', wordKanji).replace('$2', furigana)];\n              } else {\n                let kanaParts = furigana.split(seperatorRegex);\n                let kanji = wordKanji.split('');\n                if (kanaParts.length === 0 || kanaParts.length > kanji.length) {\n                  rubies = [replacementTemplate.replace('$1', wordKanji).replace('$2', furigana)];\n                } else {\n                  for (let i = 0; i < kanaParts.length - 1; i++) {\n                    if (kanji.length === 0) {\n                      break;\n                    }\n                    rubies.push(replacementTemplate.replace('$1', kanji.shift()).replace('$2', kanaParts[i]));\n                  }\n                  let lastKanaPart = kanaParts.pop();\n                  rubies.push(replacementTemplate.replace('$1', kanji.join('')).replace('$2', lastKanaPart));\n                }\n              }\n\n              return preWordTerminator + rubies.join('') + stem;\n            } else {\n              return preWordTerminator + replacementTemplate.replace('$1', wordKanji).replace('$2', furiganaText) + wordKanaSuffix;\n            }\n          } else {\n            return match;\n          }\n        });\n      });\n    }\n    return text;\n  }\n\n  function handleEscapedSpecialBrackets(text) {\n    // By default 【 and 】 cannot be escaped in markdown, this will remove backslashes from in front of them to give that effect.\n    return text.replace(/\\\\([【】])/g, '$1');\n  }\n\n  let FuriganaMD = {};\n  FuriganaMD.register = function (renderer) {\n    renderer.text = function (text) {\n      let options = {\n        furigana: true,\n        furiganaForms: \"()::{}\",\n        furiganaFallbackBrackets: \"{}\",\n        furiganaStrictMode: false,\n        furiganaAutoBracketSets: \"{}\",\n        furiganaPatternMatching: true,\n      };\n      // console.log('override text render',text);\n      // console.log('after add',addFurigana(text, options));\n      return handleEscapedSpecialBrackets(addFurigana(text, options));\n    };\n  };\n\n  return FuriganaMD;\n\n})));\n"
  },
  {
    "path": "src/libs/sync-scroll.js",
    "content": "// 左右栏同步滚动\n\n$(document).ready(function () {\n\n  let timeout;\n\n  $('div.CodeMirror-scroll, #preview').on(\"scroll\", function callback() {\n    clearTimeout(timeout);\n\n    let source = $(this),\n      target = $(source.is(\"#preview\") ? 'div.CodeMirror-scroll' : '#preview');\n\n    target.off(\"scroll\");\n\n    let source0 = source[0];\n    let target0 = target[0];\n\n    let percentage = source0.scrollTop / (source0.scrollHeight - source0.offsetHeight);\n    let height = percentage * (target0.scrollHeight - target0.offsetHeight);\n    target0.scrollTo(0, height);\n\n    timeout = setTimeout(function () {\n      target.on(\"scroll\", callback);\n    }, 100);\n  });\n\n});\n"
  }
]