[
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Bob 技术支持\n    url: https://bobtranslate.com/general/contact.html\n    about: GitHub 反馈渠道已关闭，请通过这个网页描述的方式反馈问题或建议。🙏"
  },
  {
    "path": ".gitignore",
    "content": "# Xcode\n#\n# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore\n\n## Build generated\nbuild/\nDerivedData/\n\n## Various settings\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!default.perspectivev3\nxcuserdata/\n\n## Other\n*.moved-aside\n*.xccheckout\n*.xcscmblueprint\n\n## Obj-C/Swift specific\n*.hmap\n*.ipa\n*.dSYM.zip\n*.dSYM\n\n# CocoaPods\n#\n# We recommend against adding the Pods directory to your .gitignore. However\n# you should judge for yourself, the pros and cons are mentioned at:\n# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control\n#\n# Pods/\n\n# Carthage\n#\n# Add this line if you want to avoid checking in source code from Carthage dependencies.\n# Carthage/Checkouts\n\nCarthage/Build\n\n# fastlane\n#\n# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the\n# screenshots whenever they are needed.\n# For more information about the recommended setup visit:\n# https://docs.fastlane.tools/best-practices/source-control/#source-control\n\nfastlane/report.xml\nfastlane/Preview.html\nfastlane/screenshots/**/*.png\nfastlane/test_output\n\n# Code Injection\n#\n# After new code Injection tools there's a generated folder /iOSInjectionProject\n# https://github.com/johnno1962/injectionforxcode\n\niOSInjectionProject/\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <img src=\"https://cdn.ripperhe.com/oss/master/2019/1222/bob-logo.png\" width=240 />\n</p>\n<p align=\"center\">\n\t<a href=\"https://bobtranslate.com\"><img src=\"https://img.shields.io/badge/%E5%AE%98%E6%96%B9%E7%BD%91%E7%AB%99-bobtranslate.com-brightgreen?logo=Safari\" alt=\"Website\" /></a>\n  <a href=\"https://bobtranslate.com/general/contact.html\"><img src=\"https://img.shields.io/badge/QQ%20%E7%BE%A4-696381611-blue?logo=Tencent%20QQ\" alt=\"QQ 群\" /></a>\n</p>\n\n> [!NOTE]\n> Bob 不是开源软件，本仓库曾经用于提供反馈渠道，现在请通过 [这个链接](https://bobtranslate.com/general/contact.html) 联系我们。\n\n# Bob\n\nBob 是一款 macOS 平台的 **翻译** 和 **OCR** 软件。\n\n主要特性：\n\n- **翻译功能**：划词翻译、截图翻译、输入翻译、翻译多开、自定义插件、自动识别语种、驼峰拆分、蛇形拆分、AppleScript 调用、PopClip 调用\n- **OCR 功能**：截图 OCR、静默截图 OCR、访达选图 OCR、离线识别、连续识别、二维码识别、自动复制、智能分段\n\n支持的服务：\n\n- **文本翻译**：系统翻译、火山翻译、腾讯翻译君、阿里翻译、百度翻译、有道翻译、彩云小译、小牛翻译、Google 翻译、Microsoft 翻译、Amazon 翻译、DeepL 翻译、OpenAI 翻译\n- **文本识别**：离线文本识别、火山 OCR、腾讯 OCR、腾讯图片翻译、百度 OCR、有道 OCR、Google OCR\n- **语音合成**：离线语音合成、火山语音合成\t、腾讯语音合成、Google 语音合成、Microsoft 语音合成\n\n## 安装\n\n[![Download on the Mac App Store](https://cdn.ripperhe.com/oss/master/2022/0626/Download_on_the_Mac_App_Store_Badge_US-UK_RGB_blk_092917.svg)](https://apps.apple.com/cn/app/id1630034110#?platform=mac)\n\n## 使用方法\n\n详细使用方法请直接查看文档 [👉 点此跳转文档](https://bobtranslate.com)\n\nBob 是一个菜单栏软件，启动之后，菜单栏会出现一个图标，点击菜单选项即可触发相应的功能，如下所示：\n\n<img src=\"https://cdn.ripperhe.com/oss/master/2022/0627/status_item.jpg\" alt=\"statesitem.jpg\" width=548>\n\n### 翻译功能\n\n| 方式 | 描述 | 预览 |\n| :---: | :---: | :---: |\n| 划词翻译 | 选中需要翻译的文本之后，按下划词翻译快捷键即可（默认 `⌥ + D`） | ![划词翻译-句子](https://cdn.ripperhe.com/oss/master/2022/0508/translate_selection.gif) |\n| 截图翻译 | 按下截图翻译快捷键（默认 `⌥ + S`），截取需要翻译的区域 | ![截图翻译-句子](https://cdn.ripperhe.com/oss/master/2022/0508/translate_snip.gif) |\n| 输入翻译| 按下输入翻译快捷键（默认 `⌥ + A`），输入需要翻译的文本，`Enter` 键翻译 | ![输入翻译-单词](https://cdn.ripperhe.com/oss/master/2022/0508/translate_input.gif) |\n| PopClip 调用 | 选中需要翻译的文本之后，点击 [PopClip](https://pilotmoon.com/popclip) 插件图标即可，详情见 [PopClip 调用](https://bobtranslate.com/guide/integration/popclip.html) | ![插件翻译-句子](https://cdn.ripperhe.com/oss/master/2022/0508/translate_popclip.gif) |\n\n### OCR 功能\n\n**截图识别**\n\n* 按下「截图 OCR」快捷键（默认 `⇧ + ⌥ + S`）或者点击菜单栏 Bob 图标菜单中的「截图 OCR」\n* 选中屏幕上的对应的位置\n* 松手即可开始识别\n\n<img src=\"https://cdn.ripperhe.com/oss/master/2022/0507/snip_ocr.gif\" alt=\"截图 OCR\" width=660 />\n\n**静默截图 OCR**\n\n* 按下「静默截图 OCR」快捷键（默认 `⌥` `C`）或者点击菜单栏 Bob 图标菜单中的「静默截图 OCR」\n* 选中屏幕上的对应的位置\n* 松手即可开始识别\n\n「静默截图 OCR」不会自动显示 OCR 窗口，识别完成后直接将文本拷贝到剪切板。\n\n**访达选图 OCR**\n\n按下「访达选图 OCR」快捷键（没有设置默认快捷键，可去「 Bob 偏好设置-OCR-OCR 设置」添加）或者点击菜单栏 Bob 图标菜单中的「访达选图 OCR」。\n\n在弹出的访达窗口中选中对应的图片文件（可以一次性选中多张），点击右下角「开始识别」即可。\n\n<img src=\"https://cdn.ripperhe.com/oss/master/2022/0507/file_ocr.jpg\" alt=\"访达选图 OCR\" width=600 />\n\n## 感谢\n\n* 感谢 [硅基流动](https://cloud.siliconflow.cn/i/OAwHW5q0) 为 Bob 用户免费提供 Qwen-8B 等大模型推理服务\n* 感谢 [智谱](https://bigmodel.cn/) 为 Bob 用户免费提供 GLM-4-Flash 大模型推理服务\n* 感谢 [@isee15](https://github.com/isee15/Capture-Screen-For-Multi-Screens-On-Mac) 提供最初版本截图功能的思路\n* 感谢 [@可口可乐](https://github.com/wakewon) 长期帮忙解决用户反馈\n* 感谢 [@ix4n33](https://github.com/IsaacXen) 不定期提供技术支持\n* 感谢朋友们的赞赏 [赞赏列表](https://bobtranslate.com/general/reward.html)\n* 感谢作者们发文支持 Bob（时间倒序）\n    * @火山翻译：[双厨狂喜：Bob x 火山翻译梦幻联动！](https://mp.weixin.qq.com/s/c5zwcDsCgL10m_WdBiksEQ)\n    * @奇客派：[macOS 翻译工具 Bob 大更新：支持更多翻译服务，增强 OCR 功能](https://sspai.com/post/62721)\n    * @鹿額：[截图/划词/输入都能查，快捷高效的 macOS 翻译工具: Bob](https://sspai.com/post/58249)\n    * @Newlearnerの自留地：[Bob：一款 macOS 全局翻译软件，支持划词翻译和截图翻译](https://t.me/NewlearnerChannel/3329)\n\n## 优秀软件推荐\n\n* [uPic: 一个强大的图床工具](https://github.com/gee1k/uPic)\n* [MWeb Pro: 专业的 Markdown 写作、记笔记、静态博客生成软件](https://zh.mweb.im/)\n* [Picsee: 专业的图片采集收藏、照片整理标记、查找查看、分享协同软件](https://picsee.chitaner.com)\n* [赤友 NTFS 助手: Mac用户最喜欢的 NTFS for Mac 读写软件](https://aibotech.cn/ntfs-for-mac/)\n* [赤友右键超人: Mac 右键快捷操作工具，集合右键新建文件、卸载软件、压缩文件、剪切、截图录屏超多功能，一键快速搞定！](https://aibotech.cn/right-click-menu/)\n\n"
  },
  {
    "path": "appcast.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<rss xmlns:sparkle=\"http://www.andymatuschak.org/xml-namespaces/sparkle\" version=\"2.0\">\n  <channel>\n    <title>Bob</title>\n    <item>\n      <title>0.10.3</title>\n      <pubDate>周六, 09 7月 2022 20:30:21 +0800</pubDate>\n      <sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>\n      <enclosure url=\"https://gitee.com/ripperhe/Bob/attach_files/1147719/download/Bob.zip\" sparkle:version=\"33\" sparkle:shortVersionString=\"0.10.3\" length=\"14565017\" type=\"application/octet-stream\" sparkle:edSignature=\"56yy8Z06ibEQCAwlA/BvpABs60M1HNDwLvlwe0FrIpU+50XZLVCSNVZ5ITbyYx8pX6nBCDNQp66ugMhe19LMAw==\"/>\n      <description><![CDATA[\n      <ul>\n      <li>将 App 内部所有链接指向新的社区版官网</li>\n      <li>Bob 已发布到 Mac App Store，详情请查看 <a href=\"https://bobtranslate.com/blog/2022-07-03-v1-coming.html\">这篇文章</a></li>\n      </ul>\n      ]]></description>\n    </item>\n    <item>\n      <title>0.10.2</title>\n      <pubDate>周四, 19 5月 2022 18:48:44 +0800</pubDate>\n      <sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>\n      <enclosure url=\"https://gitee.com/ripperhe/Bob/attach_files/1066760/download/Bob.zip\" sparkle:version=\"32\" sparkle:shortVersionString=\"0.10.2\" length=\"14422467\" type=\"application/octet-stream\" sparkle:edSignature=\"pKfuTcQXQo4WPxWLVrx/bb5+WJQhu7yAOqdctLnx1P8zaGHLWO8My2H1h/43Fe+jdF5mb42dHKMTwpec/zstBw==\"/>\n      <description><![CDATA[\n      <ul>\n      <li>修复翻译功能无法添加语音合成插件的问题</li>\n      </ul>\n      ]]></description>\n    </item>\n    <item>\n      <title>0.10.1</title>\n      <pubDate>周日, 15 5月 2022 13:48:28 +0800</pubDate>\n      <sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>\n      <enclosure url=\"https://gitee.com/ripperhe/Bob/attach_files/1061004/download/Bob.zip\" sparkle:version=\"30\" sparkle:shortVersionString=\"0.10.1\" length=\"14422453\" type=\"application/octet-stream\" sparkle:edSignature=\"lWsENyvEB3K7BeOW1bdR68Dz05/Yf1x5CstBay9SERJJzfOPlRFe+dFtbLnQaVGVNlBc+6EtA/vGT4KgkoPnCA==\"/>\n      <description><![CDATA[\n      <ul>\n      <li>优化在 macOS 12.3.1 调用系统翻译服务的逻辑</li>\n      </ul>\n      ]]></description>\n    </item>\n    <item>\n      <title>0.10.0</title>\n      <pubDate>周五, 13 5月 2022 21:43:36 +0800</pubDate>\n      <sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>\n      <enclosure url=\"https://gitee.com/ripperhe/Bob/attach_files/1060246/download/Bob.zip\" sparkle:version=\"29\" sparkle:shortVersionString=\"0.10.0\" length=\"14420326\" type=\"application/octet-stream\" sparkle:edSignature=\"hw//M7RI/k9DK4DqvZg3J8unc9EuU6DD/KKHScA7piI5nW1Filulf++3tqA4hT70WFIJAsooV1f3Na3pouAyCg==\"/>\n      <description><![CDATA[\n      <ul>\n      <li>新增静默截图 OCR 功能（使用静默截图 OCR 不会显示 OCR 窗口）</li>\n      <li>新增系统翻译服务，macOS 12.3.1 以上可用（实验性功能）</li>\n      <li>新增保持翻译服务折叠状态的功能（可在偏好设置开启）</li>\n      <li>修复 10.14.6 以下系统无法添加服务的问题</li>\n      <li>修复调用金山词霸服务崩溃的问题</li>\n      </ul>\n      ]]></description>\n    </item>\n    <item>\n      <title>0.9.0</title>\n      <pubDate>周日, 08 5月 2022 13:54:15 +0800</pubDate>\n      <sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>\n      <enclosure url=\"https://gitee.com/ripperhe/Bob/attach_files/1054285/download/Bob.zip\" sparkle:version=\"23\" sparkle:shortVersionString=\"0.9.0\" length=\"14329059\" type=\"application/octet-stream\" sparkle:edSignature=\"jk5QaGT8/Uz8orIhlztzxPTpujkNAEYV1iLbWkcoJ/YXxvUr/wHEQ8N/DXtrCLmMIaD+IIOVQVNsdppEVpP8DA==\"/>\n      <description><![CDATA[\n      <ul>\n      <li>全新的独立 OCR 功能</li>\n      <li>支持截图识别</li>\n      <li>支持访达选图识别</li>\n      <li>支持离线识别</li>\n      <li>支持连续识别</li>\n      <li>支持二维码识别</li>\n      <li>支持自动复制</li>\n      <li>支持智能分段</li>\n      <li>新增翻译功能取词模式设置</li>\n      </ul>\n      ]]></description>\n    </item>\n    <item>\n      <title>0.8.1</title>\n      <pubDate>周二, 29 3月 2022 22:14:00 +0800</pubDate>\n      <sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>\n      <enclosure url=\"https://gitee.com/ripperhe/Bob/attach_files/1011267/download/Bob.zip\" sparkle:version=\"18\" sparkle:shortVersionString=\"0.8.1\" length=\"13997135\" type=\"application/octet-stream\" sparkle:edSignature=\"hzf7rruUc+Faajc+EVB9b0FuCh5Uu/WnhRQ1W9aaGvYa9md8JXbYqoNZ9vhoot/ZsEmWRRxGMTnMjhreA9FLCA==\"/>\n      <description><![CDATA[\n      <ul>\n      <li>修复离线文本识别严重内存泄漏的问题</li>\n      <li>修复划词翻译偶发崩溃的问题</li>\n      </ul>\n      ]]></description>\n    </item>\n    <item>\n      <title>0.8.0</title>\n      <description><![CDATA[\n            \n            <ul>\n            <li>新增离线文本识别服务，macOS 11 以上可用</li>\n            <li>支持不保存翻译历史</li>\n            <li>支持在翻译窗口导航栏添加按钮用于快捷显隐输入框</li>\n            <li>优化对连字字符的处理</li>\n            <li>优化取词模块，支持在 Parallels Desktop 取词；<a href=\"https://ripperhe.gitee.io/bob/#/faq/parallels-desktop-support\">详情参考这篇文章</a></li>\n            <li>修复隐藏状态栏图标无效的问题</li>\n            <li>修复在文档文件夹创建 mmkv 文件夹的问题</li>\n            <li>修复金山词霸无法查询大写的问题</li>\n            </ul>\n            \n            ]]></description>\n      <pubDate>周一, 28 2月 2022 16:25:23 +0800</pubDate>\n      <sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>\n      <enclosure url=\"https://gitee.com/ripperhe/Bob/attach_files/980744/download/Bob.zip\" sparkle:version=\"16\" sparkle:shortVersionString=\"0.8.0\" length=\"13998527\" type=\"application/octet-stream\" sparkle:edSignature=\"+QgmVWTp6WhLYq97ujWzpU6Qpr1b0LTWtdzghP9fpnW77nQQW+4fxjT5RP1c/bFud1notDCjPOW60FezJa5hBw==\"/>\n    </item>\n    <item>\n      <title>0.7.0</title>\n      <description><![CDATA[\n            \n            <ul>\n            <li>全新的偏好设置页面</li>\n            <li>全新的快捷键系统，支持双击 command 等按键触发快捷键（由于换了快捷键系统，如果你之前修改过快捷键，请升级完再手动修改下）</li>\n            <li>新增翻译历史记录</li>\n            <li>新增翻译收藏夹</li>\n            <li>新增偏好设置导入导出功能</li>\n            <li>新增金山词霸查词引擎</li>\n            <li>支持隐藏菜单栏图标</li>\n            <li>支持在翻译窗口快捷切换服务</li>\n            <li>部分常用设置迁移到翻译窗口快捷控制菜单</li>\n            <li>翻译窗口 UI 微调</li>\n            <li>修复部分 PDF 软件的换行符无法替换为空格的问题</li>\n            <li>修复一些崩溃</li>\n            </ul>\n            \n            ]]></description>\n      <pubDate>周六, 27 11月 2021 17:53:18 +0800</pubDate>\n      <sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>\n      <enclosure url=\"https://gitee.com/ripperhe/Bob/attach_files/893969/download/Bob.zip\" sparkle:version=\"13\" sparkle:shortVersionString=\"0.7.0\" length=\"13799354\" type=\"application/octet-stream\" sparkle:edSignature=\"cvKPHXB6EpCmjnRFWL6n8Yz7Waf4UhfOTjVPCd0QJENu3g6FIX1xpPpKgu3k7nCxm+VfShfpzVQXfWHMljdcDw==\"/>\n    </item>\n    <item>\n      <title>0.6.1</title>\n      <description><![CDATA[\n            \n            <ul>\n            <li>修复截图翻译在 Catalina 崩溃的问题</li>\n            </ul>\n            \n            ]]></description>\n      <pubDate>周日, 14 3月 2021 16:31:12 +0800</pubDate>\n      <sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>\n      <enclosure url=\"https://gitee.com/ripperhe/Bob/attach_files/635221/download/Bob.zip\" sparkle:version=\"4\" sparkle:shortVersionString=\"0.6.1\" length=\"9457577\" type=\"application/octet-stream\" sparkle:edSignature=\"X2hiSdO5KEt05RgEuQ5febfzgyK1lkJsJXrMiPt73KAnuli4jUswmqIw/WFRttXOob4mrQ2uxYv9N8ZxMtx7DA==\"/>\n    </item>\n    <item>\n      <title>0.6.0</title>\n      <description><![CDATA[\n            \n            <ul>\n            <li>新增阿里翻译、小牛翻译、简明英汉词典增强版</li>\n            <li>新增 Google OCR</li>\n            <li>新增翻译结果为英文时将其复制为驼峰或蛇形字符串的功能（可在偏好设置开启）</li>\n            <li>修复在 Safari 浏览器下查看 Google 文档无法取词的问题</li>\n            <li>修复在部分系统下无法截图的问题</li>\n            </ul>\n            \n            ]]></description>\n      <pubDate>周日, 14 3月 2021 00:20:08 +0800</pubDate>\n      <sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>\n      <enclosure url=\"https://gitee.com/ripperhe/Bob/attach_files/634978/download/Bob.zip\" sparkle:version=\"1\" sparkle:shortVersionString=\"0.6.0\" length=\"9456536\" type=\"application/octet-stream\" sparkle:edSignature=\"tmhEtj3jw5fpB/dcRHZ6ZRCNTVfnbn0YXDPo/0b2/9yXjbVDd6DZXK3OHlNOHpiuCQwIgZnKQceBfaPjZ6v/Cg==\"/>\n    </item>\n    <item>\n      <title>0.5.4</title>\n      <description><![CDATA[\n            \n            <ul>\n            <li>适配 Big Sur 图标</li>\n            <li>新增 Google 语音合成</li>\n            <li>语音合成服务支持根据语言选择声音</li>\n            <li>彩云小译使用 https 协议</li>\n            <li>修复了 Google 翻译引号乱码的问题</li>\n            <li>修复了部分用户翻译窗口失焦后不消失的问题</li>\n            <li>修复了在 Chrome 浏览器下 Google 表格无法取词的问题</li>\n            <li>修复了百度 OCR 高精度版数字中间有空格的问题</li>\n            </ul>\n            \n            ]]></description>\n      <pubDate>周日, 27 12月 2020 23:58:32 +0800</pubDate>\n      <sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>\n      <enclosure url=\"https://gitee.com/ripperhe/Bob/attach_files/562998/download/Bob.zip\" sparkle:version=\"0.5.4.0\" sparkle:shortVersionString=\"0.5.4\" length=\"8924138\" type=\"application/octet-stream\" sparkle:edSignature=\"2jLhdAlitYAqSGQi/yQlWBxVNmgJ3Ds+PfPE5Bn0ahsQSv59Dxzsy7jKPTYUR5i9kCR+AyVnNv8qs2NvxotHAg==\"/>\n    </item>\n    <item>\n      <title>0.5.3</title>\n      <description><![CDATA[\n            \n            <ul>\n            <li>Intel 和 Apple Silicon 双架构支持</li>\n            <li>修复部分机型在 Big Sur 下菜单栏图标显示异常的问题</li>\n            <li>修复部分机型在 Chrome 浏览器下划词翻译窗口突然消失的问题</li>\n            <li>修复部分机型 Apple 语音合成无法使用及崩溃的问题</li>\n            <li>修复腾讯云语音合成无法使用的问题并增加可选项</li>\n            <li>修复 Emoji 导致有道翻译崩溃的问题</li>\n            <li>默认隐藏插件设置项的输入框文本</li>\n            <li>调整开启权限的提示</li>\n            <li>调整免费服务异常的提示</li>\n            <li>调整导出日志的提示</li>\n            </ul>\n            \n            ]]></description>\n      <pubDate>周日, 29 11月 2020 18:42:05 +0800</pubDate>\n      <sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>\n      <enclosure url=\"https://gitee.com/ripperhe/Bob/attach_files/534304/download/Bob.zip\" sparkle:version=\"0.5.3.0\" sparkle:shortVersionString=\"0.5.3\" length=\"8842780\" type=\"application/octet-stream\" sparkle:edSignature=\"HNS0DAGiFaLIJ/FGq737PruWXogeo4fEr7qnj1iukYtDiA7eA3Ic+pgVvkm6OVfmVMsOzFK6WSagFM6lsolzCA==\"/>\n    </item>\n    <item>\n      <title>0.5.2</title>\n      <description><![CDATA[\n            \n            <ul>\n            <li>修复在 macOS High Sierra (10.13) 崩溃的问题</li>\n            </ul>\n            \n            ]]></description>\n      <pubDate>周五, 09 10月 2020 17:13:20 +0800</pubDate>\n      <sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>\n      <enclosure url=\"https://gitee.com/ripperhe/Bob/attach_files/490477/download/Bob.app.zip\" sparkle:version=\"0.5.2.1\" sparkle:shortVersionString=\"0.5.2\" length=\"7251230\" type=\"application/octet-stream\" sparkle:edSignature=\"mj24h+wy55P/uVHWdVsCiqpJm5sl2hc+9mCzVENuIXkMNahN+t5ltqTb7BsbVxurKvVwJRh+v8wAn3odz6CXAg==\"/>\n    </item>\n    <item>\n      <title>0.5.1</title>\n      <description><![CDATA[\n            \n            <ul>\n            <li>接入了多家服务，通过申请私人秘钥使用</li>\n            <li>支持多开翻译，最多可同时开启5个</li>\n            <li>支持插件，可以实现自定义 API</li>\n            <li>支持驼峰拆分、蛇形拆分</li>\n            <li>UI 调整</li>\n            <li>删除了隐藏菜单栏图标的功能</li>\n            <li>删除了拖拽翻译窗口大小的功能</li>\n            <li>删除了原有的百度翻译、有道翻译和谷歌翻译</li>\n            <li>更新内容详情<a href=\"https://ripperhe.gitee.io/bob/#/blog/2020-08-03-0.5.0-new-features\">https://ripperhe.gitee.io/bob/#/blog/2020-08-03-0.5.0-new-features</a></li>\n            </ul>\n            \n            ]]></description>\n      <pubDate>周二, 06 10月 2020 22:08:34 +0800</pubDate>\n      <sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>\n      <enclosure url=\"https://gitee.com/ripperhe/Bob/attach_files/489388/download/Bob.app.zip\" sparkle:version=\"0.5.1.0\" sparkle:shortVersionString=\"0.5.1\" length=\"7251028\" type=\"application/octet-stream\" sparkle:edSignature=\"hwLGvvAN3jYAwGJoOfypTXk4WsEKbXB22tIdb5/O0hy4dyvHy4S+WfqSaNtk1DJAtaFbohXgRiLe+ts7UMT/BQ==\"/>\n    </item>\n    <item>\n      <title>0.4.0</title>\n      <description><![CDATA[\n            \n            <ul>\n            <li>支持切换和自定义菜单栏图标</li>\n            <li>支持隐藏菜单栏图标</li>\n            <li>支持显示上次翻译结果</li>\n            <li>可设置翻译窗口位置在鼠标位置、居中和菜单栏图标下方</li>\n            <li>可独立设置划词翻译和截图翻译输入框折叠状态</li>\n            <li>可设置划词取到的翻译原文中的「\\n」替换为「空格」</li>\n            <li>可设置自动复制截图翻译OCR结果</li>\n            <li>可设置自动复制翻译结果中点击后自动查询的单词</li>\n            <li>谷歌翻译新增了维吾尔语等几门语言</li>\n            <li>优化了取词功能，理论上不再影响剪切板</li>\n            <li>针对Chrome浏览器的取词做了特殊处理，开启相关权限后取词更准</li>\n            <li>修复了系统外观选为「自动」的时候，深色浅色切换异常的问题</li>\n            </ul>\n            \n            ]]></description>\n      <pubDate>周一, 16 3月 2020 14:04:51 +0800</pubDate>\n      <sparkle:minimumSystemVersion>10.12</sparkle:minimumSystemVersion>\n      <enclosure url=\"https://github.com/ripperhe/Bob/releases/download/v0.4.0/Bob.app.zip\" sparkle:version=\"0.4.0.0\" sparkle:shortVersionString=\"0.4.0\" length=\"6556874\" type=\"application/octet-stream\" sparkle:edSignature=\"zs2k5m/0iDLzxOtoHQtC0uQsSYgr2/axf8HLGT+JIW2hk8sSMO3h+fY+JAMaD7u/MnLnFe0O4qywUT//tR7aBA==\"/>\n    </item>\n    <item>\n      <title>0.3.0</title>\n      <description><![CDATA[\n            \n            <ul>\n            <li>新增了设置字体大小的功能</li>\n            <li>支持 ESC 关闭翻译页面</li>\n            <li>支持 PopClip 插件翻译</li>\n            <li>取词时去掉首尾的空白字符</li>\n            <li>输入翻译居中显示（0.2.0 之前显示在鼠标位置）</li>\n            <li>新增了未开启辅助功能权限的提示</li>\n            <li>修复了部分机型无法使用截图翻译的问题</li>\n            <li>修复了翻译窗口超出屏幕底部偶尔无法回调屏幕的问题</li>\n            <li>修复了 10.12 默认显示为深色的问题</li>\n            </ul>\n            \n            ]]></description>\n      <pubDate>周日, 02 2月 2020 21:29:58 +0800</pubDate>\n      <sparkle:minimumSystemVersion>10.12</sparkle:minimumSystemVersion>\n      <enclosure url=\"https://github.com/ripperhe/Bob/releases/download/v0.3.0/Bob.app.zip\" sparkle:version=\"0.3.0.0\" sparkle:shortVersionString=\"0.3.0\" length=\"6435399\" type=\"application/octet-stream\" sparkle:edSignature=\"wA5iVvL/j3XqbDbheDpJJAmwrj426E18p89cQq/QJn0K8UkWdnX75hn8unTmU+oFi4Gt3zzv+xFjvTAJjnrFCw==\"/>\n    </item>\n    <item>\n      <title>0.2.0</title>\n      <description><![CDATA[\n            \n            <ul>\n            <li>适配暗黑模式</li>\n            <li>翻译视图 UI 微调</li>\n            <li>新增了自动复制翻译结果的选项</li>\n            <li>快捷键提示改为用技术符号表示</li>\n            <li>截图模式可使用鼠标右键退出</li>\n            <li>弱化了翻译视图弹出影响</li>\n            <li>修复了有道词典中文查词数据解决异常的问题</li>\n            <li>解决了关闭翻译视图后无法停止朗读的问题</li>\n            <li>修复了偏好设置以及检查更新弹窗不居中的问题</li>\n            </ul>\n            \n            ]]></description>\n      <pubDate>周六, 28 12月 2019 17:22:38 +0800</pubDate>\n      <sparkle:minimumSystemVersion>10.12</sparkle:minimumSystemVersion>\n      <enclosure url=\"https://github.com/ripperhe/Bob/releases/download/0.2.0/Bob.app.zip\" sparkle:version=\"0.2.0.0\" sparkle:shortVersionString=\"0.2.0\" length=\"6413811\" type=\"application/octet-stream\" sparkle:edSignature=\"XlmsTBkRT4zIZ/K1o1eQEUPwMVYIyR+NQ+M9Ptk1g4WYfUqyRx3XLM6NynXPlFq++zp2w0DzNw3YUUsc45a+Dg==\"/>\n    </item>\n    <item>\n      <title>0.1.0</title>\n      <pubDate>周日, 22 12月 2019 18:20:35 +0800</pubDate>\n      <sparkle:minimumSystemVersion>10.12</sparkle:minimumSystemVersion>\n      <enclosure url=\"https://github.com/ripperhe/Bob/releases/download/0.1.0/Bob.app.zip\" sparkle:version=\"0.1.0.0\" sparkle:shortVersionString=\"0.1.0\" length=\"6393758\" type=\"application/octet-stream\" sparkle:edSignature=\"S/dUQQ2a8Mz7LxiV3L4Scm783xsaFyEMfeXROWW8gTlZOvTkD7Z+Qt2C62vNP4WqG5B7dgkxPQnShQBHNLGuBg==\"/>\n    </item>\n  </channel>\n</rss>"
  },
  {
    "path": "docs/.nojekyll",
    "content": ""
  },
  {
    "path": "docs/README.md",
    "content": "# Bob 官网迁移通知\n\nBob 官网之前通过 Gitee/GitHub Pages 进行部署，但是经常会有用户无法访问，现已将官网部署到我自己的服务器，域名地址也有所变更。\n\n!> **Bob 已发布到 Mac App Store，详情请查看 [这篇文章](https://bobtranslate.com/blog/2022-07-03-v1-coming.html)。**\n\n## 版本区分\n\nBob 1.0.0 版本开始，正式发布到 Mac App Store，这里简称商店版。\n\nBob 1.0.0 以下的版本（不包括 1.0.0，例如 0.10.x 等版本）发布在 GitHub/Gitee 平台，简称社区版。\n\n商店版需要付费，采用内购终身买断模式，后续我会重点维护和优化。社区版目前还可以使用，不过后续不会再维护。\n\n<img src=\"https://cdn.ripperhe.com/oss/master/2022/0709/bob-category.jpg\" alt=\"Bob 版本区分\" width=800>\n\n## 商店版官网\n\nBob 商店版官网地址为 <https://bobtranslate.com>\n\n## 社区版官网\n\nBob 社区版官网地址为 <https://v0.bobtranslate.com>\n\n如果您是通过 Bob 社区版 App 跳转到当前页面，说明您的 Bob 社区版 App 不是最新版本。请将 Bob 社区版 App 升级到社区版最新版本，升级之后 App 内的所有链接都会跳转到 Bob 社区版新的官网地址。"
  },
  {
    "path": "docs/_media/.gitkeep",
    "content": "\n"
  },
  {
    "path": "docs/_src/docsify/docsify-copy-code.js",
    "content": "/*!\n * docsify-copy-code\n * v2.1.1\n * https://github.com/jperasmus/docsify-copy-code\n * (c) 2017-2020 JP Erasmus <jperasmus11@gmail.com>\n * MIT license\n */\n!function(){\"use strict\";function s(o){return(s=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(o){return typeof o}:function(o){return o&&\"function\"==typeof Symbol&&o.constructor===Symbol&&o!==Symbol.prototype?\"symbol\":typeof o})(o)}!function(o,e){void 0===e&&(e={});var t=e.insertAt;if(o&&\"undefined\"!=typeof document){var n=document.head||document.getElementsByTagName(\"head\")[0],c=document.createElement(\"style\");c.type=\"text/css\",\"top\"===t&&n.firstChild?n.insertBefore(c,n.firstChild):n.appendChild(c),c.styleSheet?c.styleSheet.cssText=o:c.appendChild(document.createTextNode(o))}}(\".docsify-copy-code-button,.docsify-copy-code-button span{cursor:pointer;transition:all .25s ease}.docsify-copy-code-button{position:absolute;z-index:1;top:0;right:0;overflow:visible;padding:.65em .8em;border:0;border-radius:0;outline:0;font-size:1em;background:grey;background:var(--theme-color,grey);color:#fff;opacity:0}.docsify-copy-code-button span{border-radius:3px;background:inherit;pointer-events:none}.docsify-copy-code-button .error,.docsify-copy-code-button .success{position:absolute;z-index:-100;top:50%;right:0;padding:.5em .65em;font-size:.825em;opacity:0;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.docsify-copy-code-button.error .error,.docsify-copy-code-button.success .success{right:100%;opacity:1;-webkit-transform:translate(-115%,-50%);transform:translate(-115%,-50%)}.docsify-copy-code-button:focus,pre:hover .docsify-copy-code-button{opacity:1}\"),document.querySelector('link[href*=\"docsify-copy-code\"]')&&console.warn(\"[Deprecation] Link to external docsify-copy-code stylesheet is no longer necessary.\"),window.DocsifyCopyCodePlugin={init:function(){return function(o,e){o.ready(function(){console.warn(\"[Deprecation] Manually initializing docsify-copy-code using window.DocsifyCopyCodePlugin.init() is no longer necessary.\")})}}},window.$docsify=window.$docsify||{},window.$docsify.plugins=[function(o,r){o.doneEach(function(){var o=Array.apply(null,document.querySelectorAll(\"pre[data-lang]\")),c={buttonText:\"Copy to clipboard\",errorText:\"Error\",successText:\"Copied\"};r.config.copyCode&&Object.keys(c).forEach(function(t){var n=r.config.copyCode[t];\"string\"==typeof n?c[t]=n:\"object\"===s(n)&&Object.keys(n).some(function(o){var e=-1<location.href.indexOf(o);return c[t]=e?n[o]:c[t],e})});var e=['<button class=\"docsify-copy-code-button\">','<span class=\"label\">'.concat(c.buttonText,\"</span>\"),'<span class=\"error\">'.concat(c.errorText,\"</span>\"),'<span class=\"success\">'.concat(c.successText,\"</span>\"),\"</button>\"].join(\"\");o.forEach(function(o){o.insertAdjacentHTML(\"beforeend\",e)})}),o.mounted(function(){document.querySelector(\".content\").addEventListener(\"click\",function(o){if(o.target.classList.contains(\"docsify-copy-code-button\")){var e=\"BUTTON\"===o.target.tagName?o.target:o.target.parentNode,t=document.createRange(),n=e.parentNode.querySelector(\"code\"),c=window.getSelection();t.selectNode(n),c.removeAllRanges(),c.addRange(t);try{document.execCommand(\"copy\")&&(e.classList.add(\"success\"),setTimeout(function(){e.classList.remove(\"success\")},1e3))}catch(o){console.error(\"docsify-copy-code: \".concat(o)),e.classList.add(\"error\"),setTimeout(function(){e.classList.remove(\"error\")},1e3)}\"function\"==typeof(c=window.getSelection()).removeRange?c.removeRange(t):\"function\"==typeof c.removeAllRanges&&c.removeAllRanges()}})})}].concat(window.$docsify.plugins||[])}();\n//# sourceMappingURL=docsify-copy-code.min.js.map"
  },
  {
    "path": "docs/_src/docsify/search.js",
    "content": "(function () {\n    /**\n     * Converts a colon formatted string to a object with properties.\n     *\n     * This is process a provided string and look for any tokens in the format\n     * of `:name[=value]` and then convert it to a object and return.\n     * An example of this is ':include :type=code :fragment=demo' is taken and\n     * then converted to:\n     *\n     * ```\n     * {\n     *  include: '',\n     *  type: 'code',\n     *  fragment: 'demo'\n     * }\n     * ```\n     *\n     * @param {string}   str   The string to parse.\n     *\n     * @return {object}  The original string and parsed object, { str, config }.\n     */\n    function getAndRemoveConfig(str) {\n      if ( str === void 0 ) str = '';\n  \n      var config = {};\n  \n      if (str) {\n        str = str\n          .replace(/^('|\")/, '')\n          .replace(/('|\")$/, '')\n          .replace(/(?:^|\\s):([\\w-]+:?)=?([\\w-%]+)?/g, function (m, key, value) {\n            if (key.indexOf(':') === -1) {\n              config[key] = (value && value.replace(/&quot;/g, '')) || true;\n              return '';\n            }\n  \n            return m;\n          })\n          .trim();\n      }\n  \n      return { str: str, config: config };\n    }\n  \n    /* eslint-disable no-unused-vars */\n  \n    var INDEXS = {};\n  \n    var LOCAL_STORAGE = {\n      EXPIRE_KEY: 'docsify.search.expires',\n      INDEX_KEY: 'docsify.search.index',\n    };\n  \n    function resolveExpireKey(namespace) {\n      return namespace\n        ? ((LOCAL_STORAGE.EXPIRE_KEY) + \"/\" + namespace)\n        : LOCAL_STORAGE.EXPIRE_KEY;\n    }\n  \n    function resolveIndexKey(namespace) {\n      return namespace\n        ? ((LOCAL_STORAGE.INDEX_KEY) + \"/\" + namespace)\n        : LOCAL_STORAGE.INDEX_KEY;\n    }\n  \n    function escapeHtml(string) {\n      var entityMap = {\n        '&': '&amp;',\n        '<': '&lt;',\n        '>': '&gt;',\n        '\"': '&quot;',\n        \"'\": '&#39;',\n      };\n  \n      return String(string).replace(/[&<>\"']/g, function (s) { return entityMap[s]; });\n    }\n  \n    function getAllPaths(router) {\n      var paths = [];\n  \n      Docsify.dom\n        .findAll('.sidebar-nav a:not(.section-link):not([data-nosearch])')\n        .forEach(function (node) {\n          var href = node.href;\n          var originHref = node.getAttribute('href');\n          var path = router.parse(href).path;\n  \n          if (\n            path &&\n            paths.indexOf(path) === -1 &&\n            !Docsify.util.isAbsolutePath(originHref)\n          ) {\n            paths.push(path);\n          }\n        });\n  \n      return paths;\n    }\n  \n    function getTableData(token) {\n      if (!token.text && token.type === 'table') {\n        token.cells.unshift(token.header);\n        token.text = token.cells\n          .map(function(rows) {\n            return rows.join(' | ');\n          })\n          .join(' |\\n ');\n      }\n      return token.text;\n    }\n  \n    function getListData(token) {\n      if (!token.text && token.type === 'list') {\n        token.text = token.raw;\n      }\n      return token.text;\n    }\n  \n    function saveData(maxAge, expireKey, indexKey) {\n      localStorage.setItem(expireKey, Date.now() + maxAge);\n      localStorage.setItem(indexKey, JSON.stringify(INDEXS));\n    }\n  \n    function genIndex(path, content, router, depth) {\n      if ( content === void 0 ) content = '';\n  \n      var tokens = window.marked.lexer(content);\n      var slugify = window.Docsify.slugify;\n      var index = {};\n      var slug;\n      var title = '';\n  \n      tokens.forEach(function(token, tokenIndex) {\n        if (token.type === 'heading' && token.depth <= depth) {\n          var ref = getAndRemoveConfig(token.text);\n          var str = ref.str;\n          var config = ref.config;\n  \n          if (config.id) {\n            slug = router.toURL(path, { id: slugify(config.id) });\n          } else {\n            slug = router.toURL(path, { id: slugify(escapeHtml(token.text)) });\n          }\n  \n          if (str) {\n            title = str\n              .replace(/<!-- {docsify-ignore} -->/, '')\n              .replace(/{docsify-ignore}/, '')\n              .replace(/<!-- {docsify-ignore-all} -->/, '')\n              .replace(/{docsify-ignore-all}/, '')\n              .trim();\n          }\n  \n          index[slug] = { slug: slug, title: title, body: '' };\n        } else {\n          if (tokenIndex === 0) {\n            slug = router.toURL(path);\n            index[slug] = {\n              slug: slug,\n              title: path !== '/' ? path.slice(1) : 'Home Page',\n              body: token.text || '',\n            };\n          }\n  \n          if (!slug) {\n            return;\n          }\n  \n          if (!index[slug]) {\n            index[slug] = { slug: slug, title: '', body: '' };\n          } else if (index[slug].body) {\n            token.text = getTableData(token);\n            token.text = getListData(token);\n  \n            index[slug].body += '\\n' + (token.text || '');\n          } else {\n            token.text = getTableData(token);\n            token.text = getListData(token);\n  \n            index[slug].body = index[slug].body\n              ? index[slug].body + token.text\n              : token.text;\n          }\n        }\n      });\n      slugify.clear();\n      return index;\n    }\n  \n    function ignoreDiacriticalMarks(keyword) {\n      if (keyword && keyword.normalize) {\n        return keyword.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n      }\n      return keyword;\n    }\n  \n    /**\n     * @param {String} query Search query\n     * @returns {Array} Array of results\n     */\n    function search(query) {\n      var matchingResults = [];\n      var data = [];\n      Object.keys(INDEXS).forEach(function (key) {\n        data = data.concat(Object.keys(INDEXS[key]).map(function (page) { return INDEXS[key][page]; }));\n      });\n  \n      query = query.trim();\n      var keywords = query.split(/[\\s\\-，\\\\/]+/);\n      if (keywords.length !== 1) {\n        keywords = [].concat(query, keywords);\n      }\n  \n      var loop = function ( i ) {\n        var post = data[i];\n        var matchesScore = 0;\n        var resultStr = '';\n        var handlePostTitle = '';\n        var handlePostContent = '';\n        var postTitle = post.title && post.title.trim();\n        var postContent = post.body && post.body.trim();\n        var postUrl = post.slug || '';\n  \n        if (postTitle) {\n          keywords.forEach(function (keyword) {\n            // From https://github.com/sindresorhus/escape-string-regexp\n            var regEx = new RegExp(\n              escapeHtml(ignoreDiacriticalMarks(keyword)).replace(\n                /[|\\\\{}()[\\]^$+*?.]/g,\n                '\\\\$&'\n              ),\n              'gi'\n            );\n            var indexTitle = -1;\n            var indexContent = -1;\n            handlePostTitle = postTitle\n              ? escapeHtml(ignoreDiacriticalMarks(postTitle))\n              : postTitle;\n            handlePostContent = postContent\n              ? escapeHtml(ignoreDiacriticalMarks(postContent))\n              : postContent;\n  \n            indexTitle = postTitle ? handlePostTitle.search(regEx) : -1;\n            indexContent = postContent ? handlePostContent.search(regEx) : -1;\n  \n            if (indexTitle >= 0 || indexContent >= 0) {\n              matchesScore += indexTitle >= 0 ? 3 : indexContent >= 0 ? 2 : 0;\n              if (indexContent < 0) {\n                indexContent = 0;\n              }\n  \n              var start = 0;\n              var end = 0;\n  \n              start = indexContent < 11 ? 0 : indexContent - 10;\n              end = start === 0 ? 70 : indexContent + keyword.length + 60;\n  \n              if (postContent && end > postContent.length) {\n                end = postContent.length;\n              }\n  \n              var matchContent =\n                '...' +\n                handlePostContent\n                  .substring(start, end)\n                  .replace(\n                    regEx,\n                    function (word) { return (\"<em class=\\\"search-keyword\\\">\" + word + \"</em>\"); }\n                  ) +\n                '...';\n  \n              resultStr += matchContent;\n            }\n          });\n  \n          if (matchesScore > 0) {\n            var matchingPost = {\n              title: handlePostTitle,\n              content: postContent ? resultStr : '',\n              url: postUrl,\n              score: matchesScore,\n            };\n  \n            matchingResults.push(matchingPost);\n          }\n        }\n      };\n  \n      for (var i = 0; i < data.length; i++) loop( i );\n  \n      return matchingResults.sort(function (r1, r2) { return r2.score - r1.score; });\n    }\n  \n    function init(config, vm) {\n      var isAuto = config.paths === 'auto';\n      var paths = isAuto ? getAllPaths(vm.router) : config.paths;\n  \n      var namespaceSuffix = '';\n  \n      // only in auto mode\n      if (paths.length && isAuto && config.pathNamespaces) {\n        var path = paths[0];\n  \n        if (Array.isArray(config.pathNamespaces)) {\n          namespaceSuffix =\n            config.pathNamespaces.filter(\n              function (prefix) { return path.slice(0, prefix.length) === prefix; }\n            )[0] || namespaceSuffix;\n        } else if (config.pathNamespaces instanceof RegExp) {\n          var matches = path.match(config.pathNamespaces);\n  \n          if (matches) {\n            namespaceSuffix = matches[0];\n          }\n        }\n        var isExistHome = paths.indexOf(namespaceSuffix + '/') === -1;\n        var isExistReadme = paths.indexOf(namespaceSuffix + '/README') === -1;\n        if (isExistHome && isExistReadme) {\n          paths.unshift(namespaceSuffix + '/');\n        }\n      } else if (paths.indexOf('/') === -1 && paths.indexOf('/README') === -1) {\n        paths.unshift('/');\n      }\n  \n      var expireKey = resolveExpireKey(config.namespace) + namespaceSuffix;\n      var indexKey = resolveIndexKey(config.namespace) + namespaceSuffix;\n  \n      var isExpired = localStorage.getItem(expireKey) < Date.now();\n  \n      INDEXS = JSON.parse(localStorage.getItem(indexKey));\n  \n      if (isExpired) {\n        INDEXS = {};\n      } else if (!isAuto) {\n        return;\n      }\n  \n      var len = paths.length;\n      var count = 0;\n  \n      paths.forEach(function (path) {\n        if (INDEXS[path]) {\n          return count++;\n        }\n  \n        Docsify.get(vm.router.getFile(path), false, vm.config.requestHeaders).then(\n          function (result) {\n            INDEXS[path] = genIndex(path, result, vm.router, config.depth);\n            len === ++count && saveData(config.maxAge, expireKey, indexKey);\n          }\n        );\n      });\n    }\n  \n    /* eslint-disable no-unused-vars */\n  \n    var NO_DATA_TEXT = '';\n    var options;\n  \n    function style() {\n      var code = \"\\n.sidebar {\\n  padding-top: 0;\\n}\\n\\n.search {\\n  margin-bottom: 20px;\\n  padding: 6px;\\n  border-bottom: 1px solid #eee;\\n}\\n\\n.search .input-wrap {\\n  display: flex;\\n  align-items: center;\\n}\\n\\n.search .results-panel {\\n  display: none;\\n}\\n\\n.search .results-panel.show {\\n  display: block;\\n}\\n\\n.search input {\\n  outline: none;\\n  border: none;\\n  width: 100%;\\n  padding: 0 7px;\\n  line-height: 36px;\\n  font-size: 14px;\\n  border: 1px solid transparent;\\n}\\n\\n.search input:focus {\\n  box-shadow: 0 0 5px var(--theme-color, #42b983);\\n  border: 1px solid var(--theme-color, #42b983);\\n}\\n\\n.search input::-webkit-search-decoration,\\n.search input::-webkit-search-cancel-button,\\n.search input {\\n  -webkit-appearance: none;\\n  -moz-appearance: none;\\n  appearance: none;\\n}\\n.search .clear-button {\\n  cursor: pointer;\\n  width: 36px;\\n  text-align: right;\\n  display: none;\\n}\\n\\n.search .clear-button.show {\\n  display: block;\\n}\\n\\n.search .clear-button svg {\\n  transform: scale(.5);\\n}\\n\\n.search h2 {\\n  font-size: 17px;\\n  margin: 10px 0;\\n}\\n\\n.search a {\\n  text-decoration: none;\\n  color: inherit;\\n}\\n\\n.search .matching-post {\\n  border-bottom: 1px solid #eee;\\n}\\n\\n.search .matching-post:last-child {\\n  border-bottom: 0;\\n}\\n\\n.search p {\\n  font-size: 14px;\\n  overflow: hidden;\\n  text-overflow: ellipsis;\\n  display: -webkit-box;\\n  -webkit-line-clamp: 2;\\n  -webkit-box-orient: vertical;\\n}\\n\\n.search p.empty {\\n  text-align: center;\\n}\\n\\n.app-name.hide, .sidebar-nav.hide {\\n  display: none;\\n}\";\n  \n      Docsify.dom.style(code);\n    }\n  \n    function tpl(defaultValue) {\n      if ( defaultValue === void 0 ) defaultValue = '';\n  \n      var html = \"<div class=\\\"input-wrap\\\">\\n      <input type=\\\"search\\\" value=\\\"\" + defaultValue + \"\\\" aria-label=\\\"Search text\\\" />\\n      <div class=\\\"clear-button\\\">\\n        <svg width=\\\"26\\\" height=\\\"24\\\">\\n          <circle cx=\\\"12\\\" cy=\\\"12\\\" r=\\\"11\\\" fill=\\\"#ccc\\\" />\\n          <path stroke=\\\"white\\\" stroke-width=\\\"2\\\" d=\\\"M8.25,8.25,15.75,15.75\\\" />\\n          <path stroke=\\\"white\\\" stroke-width=\\\"2\\\"d=\\\"M8.25,15.75,15.75,8.25\\\" />\\n        </svg>\\n      </div>\\n    </div>\\n    <div class=\\\"results-panel\\\"></div>\\n    </div>\";\n      var el = Docsify.dom.create('div', html);\n      var aside = Docsify.dom.find('aside');\n  \n      Docsify.dom.toggleClass(el, 'search');\n      Docsify.dom.before(aside, el);\n    }\n  \n    function doSearch(value) {\n      var $search = Docsify.dom.find('div.search');\n      var $panel = Docsify.dom.find($search, '.results-panel');\n      var $clearBtn = Docsify.dom.find($search, '.clear-button');\n      var $sidebarNav = Docsify.dom.find('.sidebar-nav');\n      var $appName = Docsify.dom.find('.app-name');\n  \n      if (!value) {\n        $panel.classList.remove('show');\n        $clearBtn.classList.remove('show');\n        $panel.innerHTML = '';\n  \n        if (options.hideOtherSidebarContent) {\n          $sidebarNav && $sidebarNav.classList.remove('hide');\n          $appName && $appName.classList.remove('hide');\n        }\n  \n        return;\n      }\n  \n      var matchs = search(value);\n  \n      var html = '';\n      matchs.forEach(function (post) {\n        html += \"<div class=\\\"matching-post\\\">\\n<a href=\\\"\" + (post.url) + \"\\\">\\n<h2>\" + (post.title) + \"</h2>\\n<p>\" + (post.content) + \"</p>\\n</a>\\n</div>\";\n      });\n  \n      $panel.classList.add('show');\n      $clearBtn.classList.add('show');\n      $panel.innerHTML = html || (\"<p class=\\\"empty\\\">\" + NO_DATA_TEXT + \"</p>\");\n      if (options.hideOtherSidebarContent) {\n        $sidebarNav && $sidebarNav.classList.add('hide');\n        $appName && $appName.classList.add('hide');\n      }\n    }\n  \n    function bindEvents() {\n      var $search = Docsify.dom.find('div.search');\n      var $input = Docsify.dom.find($search, 'input');\n      var $inputWrap = Docsify.dom.find($search, '.input-wrap');\n  \n      var timeId;\n  \n      /**\n        Prevent to Fold sidebar.\n  \n        When searching on the mobile end,\n        the sidebar is collapsed when you click the INPUT box,\n        making it impossible to search.\n       */\n      Docsify.dom.on(\n        $search,\n        'click',\n        function (e) { return ['A', 'H2', 'P', 'EM'].indexOf(e.target.tagName) === -1 &&\n          e.stopPropagation(); }\n      );\n      Docsify.dom.on($input, 'input', function (e) {\n        clearTimeout(timeId);\n        timeId = setTimeout(function (_) { return doSearch(e.target.value.trim()); }, 100);\n      });\n      Docsify.dom.on($inputWrap, 'click', function (e) {\n        // Click input outside\n        if (e.target.tagName !== 'INPUT') {\n          $input.value = '';\n          doSearch();\n        }\n      });\n    }\n  \n    function updatePlaceholder(text, path) {\n      var $input = Docsify.dom.getNode('.search input[type=\"search\"]');\n  \n      if (!$input) {\n        return;\n      }\n  \n      if (typeof text === 'string') {\n        $input.placeholder = text;\n      } else {\n        var match = Object.keys(text).filter(function (key) { return path.indexOf(key) > -1; })[0];\n        $input.placeholder = text[match];\n      }\n    }\n  \n    function updateNoData(text, path) {\n      if (typeof text === 'string') {\n        NO_DATA_TEXT = text;\n      } else {\n        var match = Object.keys(text).filter(function (key) { return path.indexOf(key) > -1; })[0];\n        NO_DATA_TEXT = text[match];\n      }\n    }\n  \n    function updateOptions(opts) {\n      options = opts;\n    }\n  \n    function init$1(opts, vm) {\n      var keywords = vm.router.parse().query.s;\n  \n      updateOptions(opts);\n      style();\n      tpl(keywords);\n      bindEvents();\n      keywords && setTimeout(function (_) { return doSearch(keywords); }, 500);\n    }\n  \n    function update(opts, vm) {\n      updateOptions(opts);\n      updatePlaceholder(opts.placeholder, vm.route.path);\n      updateNoData(opts.noData, vm.route.path);\n    }\n  \n    /* eslint-disable no-unused-vars */\n  \n    var CONFIG = {\n      placeholder: 'Type to search',\n      noData: 'No Results!',\n      paths: 'auto',\n      depth: 2,\n      maxAge: 86400000, // 1 day\n      hideOtherSidebarContent: false,\n      namespace: undefined,\n      pathNamespaces: undefined,\n    };\n  \n    var install = function(hook, vm) {\n      var util = Docsify.util;\n      var opts = vm.config.search || CONFIG;\n  \n      if (Array.isArray(opts)) {\n        CONFIG.paths = opts;\n      } else if (typeof opts === 'object') {\n        CONFIG.paths = Array.isArray(opts.paths) ? opts.paths : 'auto';\n        CONFIG.maxAge = util.isPrimitive(opts.maxAge) ? opts.maxAge : CONFIG.maxAge;\n        CONFIG.placeholder = opts.placeholder || CONFIG.placeholder;\n        CONFIG.noData = opts.noData || CONFIG.noData;\n        CONFIG.depth = opts.depth || CONFIG.depth;\n        CONFIG.hideOtherSidebarContent =\n          opts.hideOtherSidebarContent || CONFIG.hideOtherSidebarContent;\n        CONFIG.namespace = opts.namespace || CONFIG.namespace;\n        CONFIG.pathNamespaces = opts.pathNamespaces || CONFIG.pathNamespaces;\n      }\n  \n      var isAuto = CONFIG.paths === 'auto';\n  \n      hook.mounted(function (_) {\n        init$1(CONFIG, vm);\n        !isAuto && init(CONFIG, vm);\n      });\n      hook.doneEach(function (_) {\n        update(CONFIG, vm);\n        isAuto && init(CONFIG, vm);\n      });\n    };\n  \n    $docsify.plugins = [].concat(install, $docsify.plugins);\n  \n  }());"
  },
  {
    "path": "docs/_src/docsify/vue.css",
    "content": "@import url(\"https://fonts.googleapis.com/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600\");*{-webkit-font-smoothing:antialiased;-webkit-overflow-scrolling:touch;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-text-size-adjust:none;-webkit-touch-callout:none;box-sizing:border-box}body:not(.ready){overflow:hidden}body:not(.ready) .app-nav,body:not(.ready)>nav,body:not(.ready) [data-cloak]{display:none}div#app{font-size:30px;font-weight:lighter;margin:40vh auto;text-align:center}div#app:empty:before{content:\"Loading...\"}.emoji{height:1.2rem;vertical-align:middle}.progress{background-color:var(--theme-color,#42b983);height:2px;left:0;position:fixed;right:0;top:0;transition:width .2s,opacity .4s;width:0;z-index:999999}.search a:hover{color:var(--theme-color,#42b983)}.search .search-keyword{color:var(--theme-color,#42b983);font-style:normal;font-weight:700}body,html{height:100%}body{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:#34495e;font-family:Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:15px;letter-spacing:0;margin:0;overflow-x:hidden}img{max-width:100%}a[disabled]{cursor:not-allowed;opacity:.6}kbd{border:1px solid #ccc;border-radius:3px;display:inline-block;font-size:12px!important;line-height:12px;margin-bottom:3px;padding:3px 5px;vertical-align:middle}li input[type=checkbox]{margin:0 .2em .25em 0;vertical-align:middle}.app-nav{margin:25px 60px 0 0;position:absolute;right:0;text-align:right;z-index:10}.app-nav.no-badge{margin-right:25px}.app-nav p{margin:0}.app-nav>a{margin:0 1rem;padding:5px 0}.app-nav li,.app-nav ul{display:inline-block;list-style:none;margin:0}.app-nav a{color:inherit;font-size:16px;text-decoration:none;transition:color .3s}.app-nav a:hover{color:var(--theme-color,#42b983)}.app-nav a.active{border-bottom:2px solid var(--theme-color,#42b983);color:var(--theme-color,#42b983)}.app-nav li{display:inline-block;margin:0 1rem;padding:5px 0;position:relative;cursor:pointer}.app-nav li ul{background-color:#fff;border:1px solid;border-color:#ddd #ddd #ccc;border-radius:4px;box-sizing:border-box;display:none;max-height:calc(100vh - 61px);overflow-y:auto;padding:10px 0;position:absolute;right:-15px;text-align:left;top:100%;white-space:nowrap}.app-nav li ul li{display:block;font-size:14px;line-height:1rem;margin:8px 14px;white-space:nowrap}.app-nav li ul a{display:block;font-size:inherit;margin:0;padding:0}.app-nav li ul a.active{border-bottom:0}.app-nav li:hover ul{display:block}.github-corner{border-bottom:0;position:fixed;right:0;text-decoration:none;top:0;z-index:1}.github-corner:hover .octo-arm{-webkit-animation:octocat-wave .56s ease-in-out;animation:octocat-wave .56s ease-in-out}.github-corner svg{color:#fff;fill:var(--theme-color,#42b983);height:80px;width:80px}main{display:block;position:relative;width:100vw;height:100%;z-index:0}main.hidden{display:none}.anchor{display:inline-block;text-decoration:none;transition:all .3s}.anchor span{color:#34495e}.anchor:hover{text-decoration:underline}.sidebar{border-right:1px solid rgba(0,0,0,.07);overflow-y:auto;padding:40px 0 0;position:absolute;top:0;bottom:0;left:0;transition:transform .25s ease-out;width:300px;z-index:20}.sidebar>h1{margin:0 auto 1rem;font-size:1.5rem;font-weight:300;text-align:center}.sidebar>h1 a{color:inherit;text-decoration:none}.sidebar>h1 .app-nav{display:block;position:static}.sidebar .sidebar-nav{line-height:2em;padding-bottom:40px}.sidebar li.collapse .app-sub-sidebar{display:none}.sidebar ul{margin:0 0 0 15px;padding:0}.sidebar li>p{font-weight:700;margin:0}.sidebar ul,.sidebar ul li{list-style:none}.sidebar ul li a{border-bottom:none;display:block}.sidebar ul li ul{padding-left:20px}.sidebar::-webkit-scrollbar{width:4px}.sidebar::-webkit-scrollbar-thumb{background:transparent;border-radius:4px}.sidebar:hover::-webkit-scrollbar-thumb{background:hsla(0,0%,53.3%,.4)}.sidebar:hover::-webkit-scrollbar-track{background:hsla(0,0%,53.3%,.1)}.sidebar-toggle{background-color:transparent;background-color:hsla(0,0%,100%,.8);border:0;outline:none;padding:10px;position:absolute;bottom:0;left:0;text-align:center;transition:opacity .3s;width:284px;z-index:30;cursor:pointer}.sidebar-toggle:hover .sidebar-toggle-button{opacity:.4}.sidebar-toggle span{background-color:var(--theme-color,#42b983);display:block;margin-bottom:4px;width:16px;height:2px}body.sticky .sidebar,body.sticky .sidebar-toggle{position:fixed}.content{padding-top:60px;position:absolute;top:0;right:0;bottom:0;left:300px;transition:left .25s ease}.markdown-section{margin:0 auto;max-width:80%;padding:30px 15px 40px;position:relative}.markdown-section>*{box-sizing:border-box;font-size:inherit}.markdown-section>:first-child{margin-top:0!important}.markdown-section hr{border:none;border-bottom:1px solid #eee;margin:2em 0}.markdown-section iframe{border:1px solid #eee;width:1px;min-width:100%}.markdown-section table{border-collapse:collapse;border-spacing:0;display:block;margin-bottom:1rem;overflow:auto;width:100%}.markdown-section th{font-weight:700}.markdown-section td,.markdown-section th{border:1px solid #ddd;padding:6px 13px}.markdown-section tr{border-top:1px solid #ccc}.markdown-section tr:nth-child(2n){background-color:#f8f8f8}.markdown-section p.tip{background-color:#f8f8f8;border-bottom-right-radius:2px;border-left:4px solid #f66;border-top-right-radius:2px;margin:2em 0;padding:12px 24px 12px 30px;position:relative}.markdown-section p.tip:before{background-color:#f66;border-radius:100%;color:#fff;content:\"!\";font-family:Dosis,Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:14px;font-weight:700;left:-12px;line-height:20px;position:absolute;height:20px;width:20px;text-align:center;top:14px}.markdown-section p.tip code{background-color:#efefef}.markdown-section p.tip em{color:#34495e}.markdown-section p.warn{background:rgba(66,185,131,.1);border-radius:2px;padding:1rem}.markdown-section ul.task-list>li{list-style-type:none}body.close .sidebar{transform:translateX(-300px)}body.close .sidebar-toggle{width:auto}body.close .content{left:0}@media print{.app-nav,.github-corner,.sidebar,.sidebar-toggle{display:none}}@media screen and (max-width:768px){.github-corner,.sidebar,.sidebar-toggle{position:fixed}.app-nav{margin-top:16px}.app-nav li ul{top:30px}main{height:auto;min-height:100vh;overflow-x:hidden}.sidebar{left:-300px;transition:transform .25s ease-out}.content{left:0;max-width:100vw;position:static;padding-top:20px;transition:transform .25s ease}.app-nav,.github-corner{transition:transform .25s ease-out}.sidebar-toggle{background-color:transparent;width:auto;padding:30px 30px 10px 10px}body.close .sidebar{transform:translateX(300px)}body.close .sidebar-toggle{background-color:hsla(0,0%,100%,.8);transition:background-color 1s;width:284px;padding:10px}body.close .content{transform:translateX(300px)}body.close .app-nav,body.close .github-corner{display:none}.github-corner:hover .octo-arm{-webkit-animation:none;animation:none}.github-corner .octo-arm{-webkit-animation:octocat-wave .56s ease-in-out;animation:octocat-wave .56s ease-in-out}}@-webkit-keyframes octocat-wave{0%,to{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@keyframes octocat-wave{0%,to{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}section.cover{align-items:center;background-position:50%;background-repeat:no-repeat;background-size:cover;height:100vh;width:100vw;display:none}section.cover.show{display:flex}section.cover.has-mask .mask{background-color:#fff;opacity:.8;position:absolute;top:0;height:100%;width:100%}section.cover .cover-main{flex:1;margin:-20px 16px 0;text-align:center;position:relative}section.cover a{color:inherit;text-decoration:none}section.cover a:hover{text-decoration:none}section.cover p{line-height:1.5rem;margin:1em 0}section.cover h1{color:inherit;font-size:2.5rem;font-weight:300;margin:.625rem 0 2.5rem;position:relative;text-align:center}section.cover h1 a{display:block}section.cover h1 small{bottom:-.4375rem;font-size:1rem;position:absolute}section.cover blockquote{font-size:1.5rem;text-align:center}section.cover ul{line-height:1.8;list-style-type:none;margin:1em auto;max-width:500px;padding:0}section.cover .cover-main>p:last-child a{border-radius:2rem;border:1px solid var(--theme-color,#42b983);box-sizing:border-box;color:var(--theme-color,#42b983);display:inline-block;font-size:1.05rem;letter-spacing:.1rem;margin:.5rem 1rem;padding:.75em 2rem;text-decoration:none;transition:all .15s ease}section.cover .cover-main>p:last-child a:last-child{background-color:var(--theme-color,#42b983);color:#fff}section.cover .cover-main>p:last-child a:last-child:hover{color:inherit;opacity:.8}section.cover .cover-main>p:last-child a:hover{color:inherit}section.cover blockquote>p>a{border-bottom:2px solid var(--theme-color,#42b983);transition:color .3s}section.cover blockquote>p>a:hover{color:var(--theme-color,#42b983)}.sidebar,body{background-color:#fff}.sidebar{color:#364149}.sidebar li{margin:6px 0}.sidebar ul li a{color:#505d6b;font-size:14px;font-weight:400;overflow:hidden;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.sidebar ul li a:hover{text-decoration:underline}.sidebar ul li ul{padding:0}.sidebar ul li.active>a{border-right:2px solid;color:var(--theme-color,#42b983);font-weight:600}.app-sub-sidebar li:before{content:\"-\";padding-right:4px;float:left}.markdown-section h1,.markdown-section h2,.markdown-section h3,.markdown-section h4,.markdown-section strong{color:#2c3e50;font-weight:600}.markdown-section a{color:var(--theme-color,#42b983);font-weight:600}.markdown-section h1{font-size:2rem;margin:0 0 1rem}.markdown-section h2{font-size:1.75rem;margin:45px 0 .8rem}.markdown-section h3{font-size:1.5rem;margin:40px 0 .6rem}.markdown-section h4{font-size:1.25rem}.markdown-section h5{font-size:1rem}.markdown-section h6{color:#777;font-size:1rem}.markdown-section figure,.markdown-section p{margin:1.2em 0}.markdown-section ol,.markdown-section p,.markdown-section ul{line-height:1.6rem;word-spacing:.05rem}.markdown-section ol,.markdown-section ul{padding-left:1.5rem}.markdown-section blockquote{border-left:4px solid var(--theme-color,#42b983);color:#858585;margin:2em 0;padding-left:20px}.markdown-section blockquote p{font-weight:600;margin-left:0}.markdown-section iframe{margin:1em 0}.markdown-section em{color:#7f8c8d}.markdown-section code,.markdown-section output:after,.markdown-section pre{font-family:Roboto Mono,Monaco,courier,monospace}.markdown-section code,.markdown-section pre{background-color:#f8f8f8}.markdown-section output,.markdown-section pre{margin:1.2em 0;position:relative}.markdown-section output,.markdown-section pre>code{border-radius:2px;display:block}.markdown-section output:after,.markdown-section pre>code{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial}.markdown-section code{border-radius:2px;color:#e96900;margin:0 2px;padding:3px 5px;white-space:pre-wrap}.markdown-section>:not(h1):not(h2):not(h3):not(h4):not(h5):not(h6) code{font-size:.8rem}.markdown-section pre{padding:0 1.4rem;line-height:1.5rem;overflow:auto;word-wrap:normal}.markdown-section pre>code{color:#525252;font-size:.8rem;padding:2.2em 5px;line-height:inherit;margin:0 2px;max-width:inherit;overflow:inherit;white-space:inherit}.markdown-section output{padding:1.7rem 1.4rem;border:1px dotted #ccc}.markdown-section output>:first-child{margin-top:0}.markdown-section output>:last-child{margin-bottom:0}.markdown-section code:after,.markdown-section code:before,.markdown-section output:after,.markdown-section output:before{letter-spacing:.05rem}.markdown-section output:after,.markdown-section pre:after{color:#ccc;font-size:.6rem;font-weight:600;height:15px;line-height:15px;padding:5px 10px 0;position:absolute;right:0;text-align:right;top:0}.markdown-section output:after,.markdown-section pre:after{content:attr(data-lang)}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#8e908c}.token.namespace{opacity:.7}.token.boolean,.token.number{color:#c76b29}.token.punctuation{color:#525252}.token.property{color:#c08b30}.token.tag{color:#2973b7}.token.string{color:var(--theme-color,#42b983)}.token.selector{color:#6679cc}.token.attr-name{color:#2973b7}.language-css .token.string,.style .token.string,.token.entity,.token.url{color:#22a2c9}.token.attr-value,.token.control,.token.directive,.token.unit{color:var(--theme-color,#42b983)}.token.function,.token.keyword{color:#e96900}.token.atrule,.token.regex,.token.statement{color:#22a2c9}.token.placeholder,.token.variable{color:#3d8fd1}.token.deleted{text-decoration:line-through}.token.inserted{border-bottom:1px dotted #202746;text-decoration:none}.token.italic{font-style:italic}.token.bold,.token.important{font-weight:700}.token.important{color:#c94922}.token.entity{cursor:help}code .token{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;min-height:1.5rem;position:relative;left:auto}"
  },
  {
    "path": "docs/_src/docsify/zoom-image.js",
    "content": "(function () {\n    /*! medium-zoom 1.0.6 | MIT License | https://github.com/francoischalifour/medium-zoom */\n    var _extends = Object.assign || function (target) {\n      var arguments$1 = arguments;\n  \n      for (var i = 1; i < arguments.length; i++) {\n        var source = arguments$1[i];\n  \n        for (var key in source) {\n          if (Object.prototype.hasOwnProperty.call(source, key)) {\n            target[key] = source[key];\n          }\n        }\n      }\n  \n      return target;\n    };\n  \n    var isSupported = function isSupported(node) {\n      return node.tagName === 'IMG';\n    };\n  \n    /* eslint-disable-next-line no-prototype-builtins */\n    var isNodeList = function isNodeList(selector) {\n      return NodeList.prototype.isPrototypeOf(selector);\n    };\n  \n    var isNode = function isNode(selector) {\n      return selector && selector.nodeType === 1;\n    };\n  \n    var isSvg = function isSvg(image) {\n      var source = image.currentSrc || image.src;\n      return source.substr(-4).toLowerCase() === '.svg';\n    };\n  \n    var getImagesFromSelector = function getImagesFromSelector(selector) {\n      try {\n        if (Array.isArray(selector)) {\n          return selector.filter(isSupported);\n        }\n  \n        if (isNodeList(selector)) {\n          // Do not use spread operator or Array.from() for IE support\n          return [].slice.call(selector).filter(isSupported);\n        }\n  \n        if (isNode(selector)) {\n          return [selector].filter(isSupported);\n        }\n  \n        if (typeof selector === 'string') {\n          // Do not use spread operator or Array.from() for IE support\n          return [].slice.call(document.querySelectorAll(selector)).filter(isSupported);\n        }\n  \n        return [];\n      } catch (err) {\n        throw new TypeError('The provided selector is invalid.\\n' + 'Expects a CSS selector, a Node element, a NodeList or an array.\\n' + 'See: https://github.com/francoischalifour/medium-zoom');\n      }\n    };\n  \n    var createOverlay = function createOverlay(background) {\n      var overlay = document.createElement('div');\n      overlay.classList.add('medium-zoom-overlay');\n      overlay.style.background = background;\n  \n      return overlay;\n    };\n  \n    var cloneTarget = function cloneTarget(template) {\n      var _template$getBounding = template.getBoundingClientRect(),\n          top = _template$getBounding.top,\n          left = _template$getBounding.left,\n          width = _template$getBounding.width,\n          height = _template$getBounding.height;\n  \n      var clone = template.cloneNode();\n      var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;\n      var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0;\n  \n      clone.removeAttribute('id');\n      clone.style.position = 'absolute';\n      clone.style.top = top + scrollTop + 'px';\n      clone.style.left = left + scrollLeft + 'px';\n      clone.style.width = width + 'px';\n      clone.style.height = height + 'px';\n      clone.style.transform = '';\n  \n      return clone;\n    };\n  \n    var createCustomEvent = function createCustomEvent(type, params) {\n      var eventParams = _extends({\n        bubbles: false,\n        cancelable: false,\n        detail: undefined\n      }, params);\n  \n      if (typeof window.CustomEvent === 'function') {\n        return new CustomEvent(type, eventParams);\n      }\n  \n      var customEvent = document.createEvent('CustomEvent');\n      customEvent.initCustomEvent(type, eventParams.bubbles, eventParams.cancelable, eventParams.detail);\n  \n      return customEvent;\n    };\n  \n    var mediumZoom = function mediumZoom(selector) {\n      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n  \n      /**\n       * Ensure the compatibility with IE11 if no Promise polyfill are used.\n       */\n      var Promise = window.Promise || function Promise(fn) {\n        function noop() {}\n        fn(noop, noop);\n      };\n  \n      var _handleClick = function _handleClick(event) {\n        var target = event.target;\n  \n  \n        if (target === overlay) {\n          close();\n          return;\n        }\n  \n        if (images.indexOf(target) === -1) {\n          return;\n        }\n  \n        toggle({ target: target });\n      };\n  \n      var _handleScroll = function _handleScroll() {\n        if (isAnimating || !active.original) {\n          return;\n        }\n  \n        var currentScroll = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;\n  \n        if (Math.abs(scrollTop - currentScroll) > zoomOptions.scrollOffset) {\n          setTimeout(close, 150);\n        }\n      };\n  \n      var _handleKeyUp = function _handleKeyUp(event) {\n        var key = event.key || event.keyCode;\n  \n        // Close if escape key is pressed\n        if (key === 'Escape' || key === 'Esc' || key === 27) {\n          close();\n        }\n      };\n  \n      var update = function update() {\n        var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n  \n        var newOptions = options;\n  \n        if (options.background) {\n          overlay.style.background = options.background;\n        }\n  \n        if (options.container && options.container instanceof Object) {\n          newOptions.container = _extends({}, zoomOptions.container, options.container);\n        }\n  \n        if (options.template) {\n          var template = isNode(options.template) ? options.template : document.querySelector(options.template);\n  \n          newOptions.template = template;\n        }\n  \n        zoomOptions = _extends({}, zoomOptions, newOptions);\n  \n        images.forEach(function (image) {\n          image.dispatchEvent(createCustomEvent('medium-zoom:update', {\n            detail: { zoom: zoom }\n          }));\n        });\n  \n        return zoom;\n      };\n  \n      var clone = function clone() {\n        var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n        return mediumZoom(_extends({}, zoomOptions, options));\n      };\n  \n      var attach = function attach() {\n        var arguments$1 = arguments;\n  \n        for (var _len = arguments.length, selectors = Array(_len), _key = 0; _key < _len; _key++) {\n          selectors[_key] = arguments$1[_key];\n        }\n  \n        var newImages = selectors.reduce(function (imagesAccumulator, currentSelector) {\n          return [].concat(imagesAccumulator, getImagesFromSelector(currentSelector));\n        }, []);\n  \n        newImages.filter(function (newImage) {\n          return images.indexOf(newImage) === -1;\n        }).forEach(function (newImage) {\n          images.push(newImage);\n          newImage.classList.add('medium-zoom-image');\n        });\n  \n        eventListeners.forEach(function (_ref) {\n          var type = _ref.type,\n              listener = _ref.listener,\n              options = _ref.options;\n  \n          newImages.forEach(function (image) {\n            image.addEventListener(type, listener, options);\n          });\n        });\n  \n        return zoom;\n      };\n  \n      var detach = function detach() {\n        var arguments$1 = arguments;\n  \n        for (var _len2 = arguments.length, selectors = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {\n          selectors[_key2] = arguments$1[_key2];\n        }\n  \n        if (active.zoomed) {\n          close();\n        }\n  \n        var imagesToDetach = selectors.length > 0 ? selectors.reduce(function (imagesAccumulator, currentSelector) {\n          return [].concat(imagesAccumulator, getImagesFromSelector(currentSelector));\n        }, []) : images;\n  \n        imagesToDetach.forEach(function (image) {\n          image.classList.remove('medium-zoom-image');\n          image.dispatchEvent(createCustomEvent('medium-zoom:detach', {\n            detail: { zoom: zoom }\n          }));\n        });\n  \n        images = images.filter(function (image) {\n          return imagesToDetach.indexOf(image) === -1;\n        });\n  \n        return zoom;\n      };\n  \n      var on = function on(type, listener) {\n        var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n  \n        images.forEach(function (image) {\n          image.addEventListener('medium-zoom:' + type, listener, options);\n        });\n  \n        eventListeners.push({ type: 'medium-zoom:' + type, listener: listener, options: options });\n  \n        return zoom;\n      };\n  \n      var off = function off(type, listener) {\n        var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n  \n        images.forEach(function (image) {\n          image.removeEventListener('medium-zoom:' + type, listener, options);\n        });\n  \n        eventListeners = eventListeners.filter(function (eventListener) {\n          return !(eventListener.type === 'medium-zoom:' + type && eventListener.listener.toString() === listener.toString());\n        });\n  \n        return zoom;\n      };\n  \n      var open = function open() {\n        var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},\n            target = _ref2.target;\n  \n        var _animate = function _animate() {\n          var container = {\n            width: document.documentElement.clientWidth,\n            height: document.documentElement.clientHeight,\n            left: 0,\n            top: 0,\n            right: 0,\n            bottom: 0\n          };\n          var viewportWidth = void 0;\n          var viewportHeight = void 0;\n  \n          if (zoomOptions.container) {\n            if (zoomOptions.container instanceof Object) {\n              // The container is given as an object with properties like width, height, left, top\n              container = _extends({}, container, zoomOptions.container);\n  \n              // We need to adjust custom options like container.right or container.bottom\n              viewportWidth = container.width - container.left - container.right - zoomOptions.margin * 2;\n              viewportHeight = container.height - container.top - container.bottom - zoomOptions.margin * 2;\n            } else {\n              // The container is given as an element\n              var zoomContainer = isNode(zoomOptions.container) ? zoomOptions.container : document.querySelector(zoomOptions.container);\n  \n              var _zoomContainer$getBou = zoomContainer.getBoundingClientRect(),\n                  _width = _zoomContainer$getBou.width,\n                  _height = _zoomContainer$getBou.height,\n                  _left = _zoomContainer$getBou.left,\n                  _top = _zoomContainer$getBou.top;\n  \n              container = _extends({}, container, {\n                width: _width,\n                height: _height,\n                left: _left,\n                top: _top\n              });\n            }\n          }\n  \n          viewportWidth = viewportWidth || container.width - zoomOptions.margin * 2;\n          viewportHeight = viewportHeight || container.height - zoomOptions.margin * 2;\n  \n          var zoomTarget = active.zoomedHd || active.original;\n          var naturalWidth = isSvg(zoomTarget) ? viewportWidth : zoomTarget.naturalWidth || viewportWidth;\n          var naturalHeight = isSvg(zoomTarget) ? viewportHeight : zoomTarget.naturalHeight || viewportHeight;\n  \n          var _zoomTarget$getBoundi = zoomTarget.getBoundingClientRect(),\n              top = _zoomTarget$getBoundi.top,\n              left = _zoomTarget$getBoundi.left,\n              width = _zoomTarget$getBoundi.width,\n              height = _zoomTarget$getBoundi.height;\n  \n          var scaleX = Math.min(naturalWidth, viewportWidth) / width;\n          var scaleY = Math.min(naturalHeight, viewportHeight) / height;\n          var scale = Math.min(scaleX, scaleY);\n          var translateX = (-left + (viewportWidth - width) / 2 + zoomOptions.margin + container.left) / scale;\n          var translateY = (-top + (viewportHeight - height) / 2 + zoomOptions.margin + container.top) / scale;\n          var transform = 'scale(' + scale + ') translate3d(' + translateX + 'px, ' + translateY + 'px, 0)';\n  \n          active.zoomed.style.transform = transform;\n  \n          if (active.zoomedHd) {\n            active.zoomedHd.style.transform = transform;\n          }\n        };\n  \n        return new Promise(function (resolve) {\n          if (target && images.indexOf(target) === -1) {\n            resolve(zoom);\n            return;\n          }\n  \n          var _handleOpenEnd = function _handleOpenEnd() {\n            isAnimating = false;\n            active.zoomed.removeEventListener('transitionend', _handleOpenEnd);\n            active.original.dispatchEvent(createCustomEvent('medium-zoom:opened', {\n              detail: { zoom: zoom }\n            }));\n  \n            resolve(zoom);\n          };\n  \n          if (active.zoomed) {\n            resolve(zoom);\n            return;\n          }\n  \n          if (target) {\n            // The zoom was triggered manually via a click\n            active.original = target;\n          } else if (images.length > 0) {\n    var _images = images;\n            active.original = _images[0];\n          } else {\n            resolve(zoom);\n            return;\n          }\n  \n          active.original.dispatchEvent(createCustomEvent('medium-zoom:open', {\n            detail: { zoom: zoom }\n          }));\n  \n          scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;\n          isAnimating = true;\n          active.zoomed = cloneTarget(active.original);\n  \n          document.body.appendChild(overlay);\n  \n          if (zoomOptions.template) {\n            var template = isNode(zoomOptions.template) ? zoomOptions.template : document.querySelector(zoomOptions.template);\n            active.template = document.createElement('div');\n            active.template.appendChild(template.content.cloneNode(true));\n  \n            document.body.appendChild(active.template);\n          }\n  \n          document.body.appendChild(active.zoomed);\n  \n          window.requestAnimationFrame(function () {\n            document.body.classList.add('medium-zoom--opened');\n          });\n  \n          active.original.classList.add('medium-zoom-image--hidden');\n          active.zoomed.classList.add('medium-zoom-image--opened');\n  \n          active.zoomed.addEventListener('click', close);\n          active.zoomed.addEventListener('transitionend', _handleOpenEnd);\n  \n          if (active.original.getAttribute('data-zoom-src')) {\n            active.zoomedHd = active.zoomed.cloneNode();\n  \n            // Reset the `scrset` property or the HD image won't load.\n            active.zoomedHd.removeAttribute('srcset');\n            active.zoomedHd.removeAttribute('sizes');\n  \n            active.zoomedHd.src = active.zoomed.getAttribute('data-zoom-src');\n  \n            active.zoomedHd.onerror = function () {\n              clearInterval(getZoomTargetSize);\n              console.warn('Unable to reach the zoom image target ' + active.zoomedHd.src);\n              active.zoomedHd = null;\n              _animate();\n            };\n  \n            // We need to access the natural size of the full HD\n            // target as fast as possible to compute the animation.\n            var getZoomTargetSize = setInterval(function () {\n              if ( active.zoomedHd.complete) {\n                clearInterval(getZoomTargetSize);\n                active.zoomedHd.classList.add('medium-zoom-image--opened');\n                active.zoomedHd.addEventListener('click', close);\n                document.body.appendChild(active.zoomedHd);\n                _animate();\n              }\n            }, 10);\n          } else if (active.original.hasAttribute('srcset')) {\n            // If an image has a `srcset` attribuet, we don't know the dimensions of the\n            // zoomed (HD) image (like when `data-zoom-src` is specified).\n            // Therefore the approach is quite similar.\n            active.zoomedHd = active.zoomed.cloneNode();\n  \n            // Resetting the sizes attribute tells the browser to load the\n            // image best fitting the current viewport size, respecting the `srcset`.\n            active.zoomedHd.removeAttribute('sizes');\n  \n            // In Firefox, the `loading` attribute needs to be set to `eager` (default\n            // value) for the load event to be fired.\n            active.zoomedHd.removeAttribute('loading');\n  \n            // Wait for the load event of the hd image. This will fire if the image\n            // is already cached.\n            var loadEventListener = active.zoomedHd.addEventListener('load', function () {\n              active.zoomedHd.removeEventListener('load', loadEventListener);\n              active.zoomedHd.classList.add('medium-zoom-image--opened');\n              active.zoomedHd.addEventListener('click', close);\n              document.body.appendChild(active.zoomedHd);\n              _animate();\n            });\n          } else {\n            _animate();\n          }\n        });\n      };\n  \n      var close = function close() {\n        return new Promise(function (resolve) {\n          if (isAnimating || !active.original) {\n            resolve(zoom);\n            return;\n          }\n  \n          var _handleCloseEnd = function _handleCloseEnd() {\n            active.original.classList.remove('medium-zoom-image--hidden');\n            document.body.removeChild(active.zoomed);\n            if (active.zoomedHd) {\n              document.body.removeChild(active.zoomedHd);\n            }\n            document.body.removeChild(overlay);\n            active.zoomed.classList.remove('medium-zoom-image--opened');\n            if (active.template) {\n              document.body.removeChild(active.template);\n            }\n  \n            isAnimating = false;\n            active.zoomed.removeEventListener('transitionend', _handleCloseEnd);\n  \n            active.original.dispatchEvent(createCustomEvent('medium-zoom:closed', {\n              detail: { zoom: zoom }\n            }));\n  \n            active.original = null;\n            active.zoomed = null;\n            active.zoomedHd = null;\n            active.template = null;\n  \n            resolve(zoom);\n          };\n  \n          isAnimating = true;\n          document.body.classList.remove('medium-zoom--opened');\n          active.zoomed.style.transform = '';\n  \n          if (active.zoomedHd) {\n            active.zoomedHd.style.transform = '';\n          }\n  \n          // Fade out the template so it's not too abrupt\n          if (active.template) {\n            active.template.style.transition = 'opacity 150ms';\n            active.template.style.opacity = 0;\n          }\n  \n          active.original.dispatchEvent(createCustomEvent('medium-zoom:close', {\n            detail: { zoom: zoom }\n          }));\n  \n          active.zoomed.addEventListener('transitionend', _handleCloseEnd);\n        });\n      };\n  \n      var toggle = function toggle() {\n        var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},\n            target = _ref3.target;\n  \n        if (active.original) {\n          return close();\n        }\n  \n        return open({ target: target });\n      };\n  \n      var getOptions = function getOptions() {\n        return zoomOptions;\n      };\n  \n      var getImages = function getImages() {\n        return images;\n      };\n  \n      var getZoomedImage = function getZoomedImage() {\n        return active.original;\n      };\n  \n      var images = [];\n      var eventListeners = [];\n      var isAnimating = false;\n      var scrollTop = 0;\n      var zoomOptions = options;\n      var active = {\n        original: null,\n        zoomed: null,\n        zoomedHd: null,\n        template: null\n  \n        // If the selector is omitted, it's replaced by the options\n      };if (Object.prototype.toString.call(selector) === '[object Object]') {\n        zoomOptions = selector;\n      } else if (selector || typeof selector === 'string' // to process empty string as a selector\n      ) {\n          attach(selector);\n        }\n  \n      // Apply the default option values\n      zoomOptions = _extends({\n        margin: 0,\n        background: '#fff',\n        scrollOffset: 40,\n        container: null,\n        template: null\n      }, zoomOptions);\n  \n      var overlay = createOverlay(zoomOptions.background);\n  \n      document.addEventListener('click', _handleClick);\n      document.addEventListener('keyup', _handleKeyUp);\n      document.addEventListener('scroll', _handleScroll);\n      window.addEventListener('resize', close);\n  \n      var zoom = {\n        open: open,\n        close: close,\n        toggle: toggle,\n        update: update,\n        clone: clone,\n        attach: attach,\n        detach: detach,\n        on: on,\n        off: off,\n        getOptions: getOptions,\n        getImages: getImages,\n        getZoomedImage: getZoomedImage\n      };\n  \n      return zoom;\n    };\n  \n    function styleInject(css, ref) {\n      if ( ref === void 0 ) { ref = {}; }\n      var insertAt = ref.insertAt;\n  \n      if (!css || typeof document === 'undefined') { return; }\n  \n      var head = document.head || document.getElementsByTagName('head')[0];\n      var style = document.createElement('style');\n      style.type = 'text/css';\n  \n      if (insertAt === 'top') {\n        if (head.firstChild) {\n          head.insertBefore(style, head.firstChild);\n        } else {\n          head.appendChild(style);\n        }\n      } else {\n        head.appendChild(style);\n      }\n  \n      if (style.styleSheet) {\n        style.styleSheet.cssText = css;\n      } else {\n        style.appendChild(document.createTextNode(css));\n      }\n    }\n  \n    var css = \".medium-zoom-overlay{position:fixed;top:0;right:0;bottom:0;left:0;opacity:0;transition:opacity .3s;will-change:opacity}.medium-zoom--opened .medium-zoom-overlay{cursor:pointer;cursor:zoom-out;opacity:1}.medium-zoom-image{cursor:pointer;cursor:zoom-in;transition:transform .3s cubic-bezier(.2,0,.2,1)!important}.medium-zoom-image--hidden{visibility:hidden}.medium-zoom-image--opened{position:relative;cursor:pointer;cursor:zoom-out;will-change:transform}\";\n    styleInject(css);\n  \n    /* eslint-disable no-unused-vars */\n  \n    var matchesSelector =\n      Element.prototype.matches ||\n      Element.prototype.webkitMatchesSelector ||\n      Element.prototype.msMatchesSelector;\n  \n    function install(hook) {\n      var zoom;\n  \n      hook.doneEach(function (_) {\n        var elms = Array.apply(\n          null,\n          document.querySelectorAll(\n            '.markdown-section img:not(.emoji):not([data-no-zoom])'\n          )\n        );\n  \n        elms = elms.filter(function (elm) { return matchesSelector.call(elm, 'a img') === false; });\n  \n        if (zoom) {\n          zoom.detach();\n        }\n  \n        zoom = mediumZoom(elms);\n      });\n    }\n  \n    $docsify.plugins = [].concat(install, $docsify.plugins);\n  \n  }());"
  },
  {
    "path": "docs/_src/source.txt",
    "content": "https://cdn.jsdelivr.net/npm/docsify/lib/themes/vue.css\nhttps://cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js\nhttps://cdn.jsdelivr.net/npm/docsify/lib/plugins/search.js\nhttps://cdn.jsdelivr.net/npm/docsify/lib/plugins/zoom-image.js\nhttps://cdn.jsdelivr.net/npm/docsify-copy-code\n\nhttps://cdn.jsdelivr.net/npm/prismjs/components/prism-javascript.min.js\nhttps://cdn.jsdelivr.net/npm/prismjs/components/prism-json.min.js\nhttps://cdn.jsdelivr.net/npm/prismjs/components/prism-applescript.min.js\nhttps://cdn.jsdelivr.net/npm/prismjs/components/prism-bash.min.js"
  },
  {
    "path": "docs/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\">\n  <!-- 网站标题 -->\n  <title>Bob</title>\n  <!-- 网站icon -->\n  <link rel=\"icon\" href=\"_media/favicon.ico\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\" />\n  <meta name=\"description\" content=\"Description\">\n  <meta name=\"viewport\"\n    content=\"width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0\">\n  <!-- Theme 主题设置 [vue.css] [buble.css] [dark.css] [pure.css] -->\n  <link rel=\"stylesheet\" href=\"_src/docsify/vue.css\">\n</head>\n\n<body>\n  <!-- loading显示的文字 -->\n  <div id=\"app\">Loading...</div>\n  <script>\n    window.$docsify = {\n      // ************************* Configuration *************************\n      // https://docsify.js.org/#/zh-cn/configuration\n      // --> 首页 「README.md」\n      homepage: 'README.md',\n      // --> 封面 「_coverpage.md」\n      coverpage: false,\n      // --> 侧边栏 [_sidebar.md]\n      loadSidebar: false,\n      // 说实话，没搞懂\n      maxLevel: 4,\n      // 设置侧边栏中，当前文章的最大目录层级；不设置，则不会显示当前文章目录\n      subMaxLevel: 2,\n      // --> 导航栏 [_navbar.md]\n      loadNavbar: false,\n      // 小屏设备下合并导航栏到侧边栏\n      mergeNavbar: true,\n      // --> 404页面 「_404.md」\n      notFoundPage: \"README.md\",\n      // 隐藏侧边栏\n      hideSidebar: true,\n\n      // 网址logo\n      // logo: '/_media/icon.svg',\n      // 文档标题，显示在侧边栏顶部\n      name: 'Bob',\n      // 仓库地址（设置之后，会在页面右上角渲染一个 GitHub Corner 挂件）\n      // repo: 'ripperhe/Bob',\n      // 页面切换之后，自动跳转到顶部\n      auto2top: true,\n      // 格式化\n      formatUpdated: '{MM}/{DD} {HH}:{mm}',\n\n      // ************************* Plugins *************************\n      // https://docsify.js.org/#/zh-cn/plugins\n      // 全文搜索 - Search\n      search: {\n        placeholder: '搜索',\n        noData: '找不到结果',\n      },\n      plugins: [\n        // Edit On Github\n        function (hook, vm) {\n          hook.beforeEach(function (html) {\n            var chatHtml = '[:rocket: 点击此处联系我](https://bobtranslate.com/general/contact.html)'\n            return html\n              + '\\n\\n----\\n\\n'\n              + chatHtml\n          })\n        },\n      ]\n    }\n  </script>\n  <script src=\"_src/docsify/docsify.min.js\"></script>\n  <!-- ************************* Plugins ************************* -->\n  <!-- Plugins：全文搜索 -->\n  <script src=\"_src/docsify/search.js\"></script>\n  <!-- Plugins：图片缩放 - Zoom image -->\n  <script src=\"_src/docsify/zoom-image.js\"></script>\n  <!-- 点击代码块右上角复制代码 -->\n  <script src=\"_src/docsify/docsify-copy-code.js\"></script>\n  <!-- Plugins：代码高亮 prism https://github.com/PrismJS/prism -->\n  <script src=\"_src/prismjs/prism-javascript.min.js\"></script>\n  <script src=\"_src/prismjs/prism-json.min.js\"></script>\n  <script src=\"_src/prismjs/prism-applescript.min.js\"></script>\n  <script src=\"_src/prismjs/prism-bash.min.js\"></script>\n  <!-- Global site tag (gtag.js) - Google Analytics -->\n  <script async src=\"https://www.googletagmanager.com/gtag/js?id=G-4EK44EJ8FX\"></script>\n  <script>\n    window.dataLayer = window.dataLayer || [];\n    function gtag() { dataLayer.push(arguments); }\n    gtag('js', new Date());\n\n    gtag('config', 'G-4EK44EJ8FX');\n  </script>\n</body>\n\n</html>"
  }
]