Repository: ripperhe/Bob
Branch: master
Commit: 3e51986a299c
Files: 13
Total size: 80.4 KB
Directory structure:
gitextract_06josyrd/
├── .github/
│ └── ISSUE_TEMPLATE/
│ └── config.yml
├── .gitignore
├── README.md
├── appcast.xml
└── docs/
├── .nojekyll
├── README.md
├── _media/
│ └── .gitkeep
├── _src/
│ ├── docsify/
│ │ ├── docsify-copy-code.js
│ │ ├── search.js
│ │ ├── vue.css
│ │ └── zoom-image.js
│ └── source.txt
└── index.html
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: Bob 技术支持
url: https://bobtranslate.com/general/contact.html
about: GitHub 反馈渠道已关闭,请通过这个网页描述的方式反馈问题或建议。🙏
================================================
FILE: .gitignore
================================================
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
build/
DerivedData/
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
*.ipa
*.dSYM.zip
*.dSYM
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output
# Code Injection
#
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode
iOSInjectionProject/
================================================
FILE: README.md
================================================
<p align="center">
<img src="https://cdn.ripperhe.com/oss/master/2019/1222/bob-logo.png" width=240 />
</p>
<p align="center">
<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>
<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>
</p>
> [!NOTE]
> Bob 不是开源软件,本仓库曾经用于提供反馈渠道,现在请通过 [这个链接](https://bobtranslate.com/general/contact.html) 联系我们。
# Bob
Bob 是一款 macOS 平台的 **翻译** 和 **OCR** 软件。
主要特性:
- **翻译功能**:划词翻译、截图翻译、输入翻译、翻译多开、自定义插件、自动识别语种、驼峰拆分、蛇形拆分、AppleScript 调用、PopClip 调用
- **OCR 功能**:截图 OCR、静默截图 OCR、访达选图 OCR、离线识别、连续识别、二维码识别、自动复制、智能分段
支持的服务:
- **文本翻译**:系统翻译、火山翻译、腾讯翻译君、阿里翻译、百度翻译、有道翻译、彩云小译、小牛翻译、Google 翻译、Microsoft 翻译、Amazon 翻译、DeepL 翻译、OpenAI 翻译
- **文本识别**:离线文本识别、火山 OCR、腾讯 OCR、腾讯图片翻译、百度 OCR、有道 OCR、Google OCR
- **语音合成**:离线语音合成、火山语音合成 、腾讯语音合成、Google 语音合成、Microsoft 语音合成
## 安装
[](https://apps.apple.com/cn/app/id1630034110#?platform=mac)
## 使用方法
详细使用方法请直接查看文档 [👉 点此跳转文档](https://bobtranslate.com)
Bob 是一个菜单栏软件,启动之后,菜单栏会出现一个图标,点击菜单选项即可触发相应的功能,如下所示:
<img src="https://cdn.ripperhe.com/oss/master/2022/0627/status_item.jpg" alt="statesitem.jpg" width=548>
### 翻译功能
| 方式 | 描述 | 预览 |
| :---: | :---: | :---: |
| 划词翻译 | 选中需要翻译的文本之后,按下划词翻译快捷键即可(默认 `⌥ + D`) |  |
| 截图翻译 | 按下截图翻译快捷键(默认 `⌥ + S`),截取需要翻译的区域 |  |
| 输入翻译| 按下输入翻译快捷键(默认 `⌥ + A`),输入需要翻译的文本,`Enter` 键翻译 |  |
| PopClip 调用 | 选中需要翻译的文本之后,点击 [PopClip](https://pilotmoon.com/popclip) 插件图标即可,详情见 [PopClip 调用](https://bobtranslate.com/guide/integration/popclip.html) |  |
### OCR 功能
**截图识别**
* 按下「截图 OCR」快捷键(默认 `⇧ + ⌥ + S`)或者点击菜单栏 Bob 图标菜单中的「截图 OCR」
* 选中屏幕上的对应的位置
* 松手即可开始识别
<img src="https://cdn.ripperhe.com/oss/master/2022/0507/snip_ocr.gif" alt="截图 OCR" width=660 />
**静默截图 OCR**
* 按下「静默截图 OCR」快捷键(默认 `⌥` `C`)或者点击菜单栏 Bob 图标菜单中的「静默截图 OCR」
* 选中屏幕上的对应的位置
* 松手即可开始识别
「静默截图 OCR」不会自动显示 OCR 窗口,识别完成后直接将文本拷贝到剪切板。
**访达选图 OCR**
按下「访达选图 OCR」快捷键(没有设置默认快捷键,可去「 Bob 偏好设置-OCR-OCR 设置」添加)或者点击菜单栏 Bob 图标菜单中的「访达选图 OCR」。
在弹出的访达窗口中选中对应的图片文件(可以一次性选中多张),点击右下角「开始识别」即可。
<img src="https://cdn.ripperhe.com/oss/master/2022/0507/file_ocr.jpg" alt="访达选图 OCR" width=600 />
## 感谢
* 感谢 [硅基流动](https://cloud.siliconflow.cn/i/OAwHW5q0) 为 Bob 用户免费提供 Qwen-8B 等大模型推理服务
* 感谢 [智谱](https://bigmodel.cn/) 为 Bob 用户免费提供 GLM-4-Flash 大模型推理服务
* 感谢 [@isee15](https://github.com/isee15/Capture-Screen-For-Multi-Screens-On-Mac) 提供最初版本截图功能的思路
* 感谢 [@可口可乐](https://github.com/wakewon) 长期帮忙解决用户反馈
* 感谢 [@ix4n33](https://github.com/IsaacXen) 不定期提供技术支持
* 感谢朋友们的赞赏 [赞赏列表](https://bobtranslate.com/general/reward.html)
* 感谢作者们发文支持 Bob(时间倒序)
* @火山翻译:[双厨狂喜:Bob x 火山翻译梦幻联动!](https://mp.weixin.qq.com/s/c5zwcDsCgL10m_WdBiksEQ)
* @奇客派:[macOS 翻译工具 Bob 大更新:支持更多翻译服务,增强 OCR 功能](https://sspai.com/post/62721)
* @鹿額:[截图/划词/输入都能查,快捷高效的 macOS 翻译工具: Bob](https://sspai.com/post/58249)
* @Newlearnerの自留地:[Bob:一款 macOS 全局翻译软件,支持划词翻译和截图翻译](https://t.me/NewlearnerChannel/3329)
## 优秀软件推荐
* [uPic: 一个强大的图床工具](https://github.com/gee1k/uPic)
* [MWeb Pro: 专业的 Markdown 写作、记笔记、静态博客生成软件](https://zh.mweb.im/)
* [Picsee: 专业的图片采集收藏、照片整理标记、查找查看、分享协同软件](https://picsee.chitaner.com)
* [赤友 NTFS 助手: Mac用户最喜欢的 NTFS for Mac 读写软件](https://aibotech.cn/ntfs-for-mac/)
* [赤友右键超人: Mac 右键快捷操作工具,集合右键新建文件、卸载软件、压缩文件、剪切、截图录屏超多功能,一键快速搞定!](https://aibotech.cn/right-click-menu/)
================================================
FILE: appcast.xml
================================================
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<rss xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" version="2.0">
<channel>
<title>Bob</title>
<item>
<title>0.10.3</title>
<pubDate>周六, 09 7月 2022 20:30:21 +0800</pubDate>
<sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>
<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=="/>
<description><![CDATA[
<ul>
<li>将 App 内部所有链接指向新的社区版官网</li>
<li>Bob 已发布到 Mac App Store,详情请查看 <a href="https://bobtranslate.com/blog/2022-07-03-v1-coming.html">这篇文章</a></li>
</ul>
]]></description>
</item>
<item>
<title>0.10.2</title>
<pubDate>周四, 19 5月 2022 18:48:44 +0800</pubDate>
<sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>
<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=="/>
<description><![CDATA[
<ul>
<li>修复翻译功能无法添加语音合成插件的问题</li>
</ul>
]]></description>
</item>
<item>
<title>0.10.1</title>
<pubDate>周日, 15 5月 2022 13:48:28 +0800</pubDate>
<sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>
<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=="/>
<description><![CDATA[
<ul>
<li>优化在 macOS 12.3.1 调用系统翻译服务的逻辑</li>
</ul>
]]></description>
</item>
<item>
<title>0.10.0</title>
<pubDate>周五, 13 5月 2022 21:43:36 +0800</pubDate>
<sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>
<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=="/>
<description><![CDATA[
<ul>
<li>新增静默截图 OCR 功能(使用静默截图 OCR 不会显示 OCR 窗口)</li>
<li>新增系统翻译服务,macOS 12.3.1 以上可用(实验性功能)</li>
<li>新增保持翻译服务折叠状态的功能(可在偏好设置开启)</li>
<li>修复 10.14.6 以下系统无法添加服务的问题</li>
<li>修复调用金山词霸服务崩溃的问题</li>
</ul>
]]></description>
</item>
<item>
<title>0.9.0</title>
<pubDate>周日, 08 5月 2022 13:54:15 +0800</pubDate>
<sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>
<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=="/>
<description><![CDATA[
<ul>
<li>全新的独立 OCR 功能</li>
<li>支持截图识别</li>
<li>支持访达选图识别</li>
<li>支持离线识别</li>
<li>支持连续识别</li>
<li>支持二维码识别</li>
<li>支持自动复制</li>
<li>支持智能分段</li>
<li>新增翻译功能取词模式设置</li>
</ul>
]]></description>
</item>
<item>
<title>0.8.1</title>
<pubDate>周二, 29 3月 2022 22:14:00 +0800</pubDate>
<sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>
<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=="/>
<description><![CDATA[
<ul>
<li>修复离线文本识别严重内存泄漏的问题</li>
<li>修复划词翻译偶发崩溃的问题</li>
</ul>
]]></description>
</item>
<item>
<title>0.8.0</title>
<description><![CDATA[
<ul>
<li>新增离线文本识别服务,macOS 11 以上可用</li>
<li>支持不保存翻译历史</li>
<li>支持在翻译窗口导航栏添加按钮用于快捷显隐输入框</li>
<li>优化对连字字符的处理</li>
<li>优化取词模块,支持在 Parallels Desktop 取词;<a href="https://ripperhe.gitee.io/bob/#/faq/parallels-desktop-support">详情参考这篇文章</a></li>
<li>修复隐藏状态栏图标无效的问题</li>
<li>修复在文档文件夹创建 mmkv 文件夹的问题</li>
<li>修复金山词霸无法查询大写的问题</li>
</ul>
]]></description>
<pubDate>周一, 28 2月 2022 16:25:23 +0800</pubDate>
<sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>
<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=="/>
</item>
<item>
<title>0.7.0</title>
<description><![CDATA[
<ul>
<li>全新的偏好设置页面</li>
<li>全新的快捷键系统,支持双击 command 等按键触发快捷键(由于换了快捷键系统,如果你之前修改过快捷键,请升级完再手动修改下)</li>
<li>新增翻译历史记录</li>
<li>新增翻译收藏夹</li>
<li>新增偏好设置导入导出功能</li>
<li>新增金山词霸查词引擎</li>
<li>支持隐藏菜单栏图标</li>
<li>支持在翻译窗口快捷切换服务</li>
<li>部分常用设置迁移到翻译窗口快捷控制菜单</li>
<li>翻译窗口 UI 微调</li>
<li>修复部分 PDF 软件的换行符无法替换为空格的问题</li>
<li>修复一些崩溃</li>
</ul>
]]></description>
<pubDate>周六, 27 11月 2021 17:53:18 +0800</pubDate>
<sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>
<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=="/>
</item>
<item>
<title>0.6.1</title>
<description><![CDATA[
<ul>
<li>修复截图翻译在 Catalina 崩溃的问题</li>
</ul>
]]></description>
<pubDate>周日, 14 3月 2021 16:31:12 +0800</pubDate>
<sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>
<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=="/>
</item>
<item>
<title>0.6.0</title>
<description><![CDATA[
<ul>
<li>新增阿里翻译、小牛翻译、简明英汉词典增强版</li>
<li>新增 Google OCR</li>
<li>新增翻译结果为英文时将其复制为驼峰或蛇形字符串的功能(可在偏好设置开启)</li>
<li>修复在 Safari 浏览器下查看 Google 文档无法取词的问题</li>
<li>修复在部分系统下无法截图的问题</li>
</ul>
]]></description>
<pubDate>周日, 14 3月 2021 00:20:08 +0800</pubDate>
<sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>
<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=="/>
</item>
<item>
<title>0.5.4</title>
<description><![CDATA[
<ul>
<li>适配 Big Sur 图标</li>
<li>新增 Google 语音合成</li>
<li>语音合成服务支持根据语言选择声音</li>
<li>彩云小译使用 https 协议</li>
<li>修复了 Google 翻译引号乱码的问题</li>
<li>修复了部分用户翻译窗口失焦后不消失的问题</li>
<li>修复了在 Chrome 浏览器下 Google 表格无法取词的问题</li>
<li>修复了百度 OCR 高精度版数字中间有空格的问题</li>
</ul>
]]></description>
<pubDate>周日, 27 12月 2020 23:58:32 +0800</pubDate>
<sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>
<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=="/>
</item>
<item>
<title>0.5.3</title>
<description><![CDATA[
<ul>
<li>Intel 和 Apple Silicon 双架构支持</li>
<li>修复部分机型在 Big Sur 下菜单栏图标显示异常的问题</li>
<li>修复部分机型在 Chrome 浏览器下划词翻译窗口突然消失的问题</li>
<li>修复部分机型 Apple 语音合成无法使用及崩溃的问题</li>
<li>修复腾讯云语音合成无法使用的问题并增加可选项</li>
<li>修复 Emoji 导致有道翻译崩溃的问题</li>
<li>默认隐藏插件设置项的输入框文本</li>
<li>调整开启权限的提示</li>
<li>调整免费服务异常的提示</li>
<li>调整导出日志的提示</li>
</ul>
]]></description>
<pubDate>周日, 29 11月 2020 18:42:05 +0800</pubDate>
<sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>
<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=="/>
</item>
<item>
<title>0.5.2</title>
<description><![CDATA[
<ul>
<li>修复在 macOS High Sierra (10.13) 崩溃的问题</li>
</ul>
]]></description>
<pubDate>周五, 09 10月 2020 17:13:20 +0800</pubDate>
<sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>
<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=="/>
</item>
<item>
<title>0.5.1</title>
<description><![CDATA[
<ul>
<li>接入了多家服务,通过申请私人秘钥使用</li>
<li>支持多开翻译,最多可同时开启5个</li>
<li>支持插件,可以实现自定义 API</li>
<li>支持驼峰拆分、蛇形拆分</li>
<li>UI 调整</li>
<li>删除了隐藏菜单栏图标的功能</li>
<li>删除了拖拽翻译窗口大小的功能</li>
<li>删除了原有的百度翻译、有道翻译和谷歌翻译</li>
<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>
</ul>
]]></description>
<pubDate>周二, 06 10月 2020 22:08:34 +0800</pubDate>
<sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>
<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=="/>
</item>
<item>
<title>0.4.0</title>
<description><![CDATA[
<ul>
<li>支持切换和自定义菜单栏图标</li>
<li>支持隐藏菜单栏图标</li>
<li>支持显示上次翻译结果</li>
<li>可设置翻译窗口位置在鼠标位置、居中和菜单栏图标下方</li>
<li>可独立设置划词翻译和截图翻译输入框折叠状态</li>
<li>可设置划词取到的翻译原文中的「\n」替换为「空格」</li>
<li>可设置自动复制截图翻译OCR结果</li>
<li>可设置自动复制翻译结果中点击后自动查询的单词</li>
<li>谷歌翻译新增了维吾尔语等几门语言</li>
<li>优化了取词功能,理论上不再影响剪切板</li>
<li>针对Chrome浏览器的取词做了特殊处理,开启相关权限后取词更准</li>
<li>修复了系统外观选为「自动」的时候,深色浅色切换异常的问题</li>
</ul>
]]></description>
<pubDate>周一, 16 3月 2020 14:04:51 +0800</pubDate>
<sparkle:minimumSystemVersion>10.12</sparkle:minimumSystemVersion>
<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=="/>
</item>
<item>
<title>0.3.0</title>
<description><![CDATA[
<ul>
<li>新增了设置字体大小的功能</li>
<li>支持 ESC 关闭翻译页面</li>
<li>支持 PopClip 插件翻译</li>
<li>取词时去掉首尾的空白字符</li>
<li>输入翻译居中显示(0.2.0 之前显示在鼠标位置)</li>
<li>新增了未开启辅助功能权限的提示</li>
<li>修复了部分机型无法使用截图翻译的问题</li>
<li>修复了翻译窗口超出屏幕底部偶尔无法回调屏幕的问题</li>
<li>修复了 10.12 默认显示为深色的问题</li>
</ul>
]]></description>
<pubDate>周日, 02 2月 2020 21:29:58 +0800</pubDate>
<sparkle:minimumSystemVersion>10.12</sparkle:minimumSystemVersion>
<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=="/>
</item>
<item>
<title>0.2.0</title>
<description><![CDATA[
<ul>
<li>适配暗黑模式</li>
<li>翻译视图 UI 微调</li>
<li>新增了自动复制翻译结果的选项</li>
<li>快捷键提示改为用技术符号表示</li>
<li>截图模式可使用鼠标右键退出</li>
<li>弱化了翻译视图弹出影响</li>
<li>修复了有道词典中文查词数据解决异常的问题</li>
<li>解决了关闭翻译视图后无法停止朗读的问题</li>
<li>修复了偏好设置以及检查更新弹窗不居中的问题</li>
</ul>
]]></description>
<pubDate>周六, 28 12月 2019 17:22:38 +0800</pubDate>
<sparkle:minimumSystemVersion>10.12</sparkle:minimumSystemVersion>
<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=="/>
</item>
<item>
<title>0.1.0</title>
<pubDate>周日, 22 12月 2019 18:20:35 +0800</pubDate>
<sparkle:minimumSystemVersion>10.12</sparkle:minimumSystemVersion>
<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=="/>
</item>
</channel>
</rss>
================================================
FILE: docs/.nojekyll
================================================
================================================
FILE: docs/README.md
================================================
# Bob 官网迁移通知
Bob 官网之前通过 Gitee/GitHub Pages 进行部署,但是经常会有用户无法访问,现已将官网部署到我自己的服务器,域名地址也有所变更。
!> **Bob 已发布到 Mac App Store,详情请查看 [这篇文章](https://bobtranslate.com/blog/2022-07-03-v1-coming.html)。**
## 版本区分
Bob 1.0.0 版本开始,正式发布到 Mac App Store,这里简称商店版。
Bob 1.0.0 以下的版本(不包括 1.0.0,例如 0.10.x 等版本)发布在 GitHub/Gitee 平台,简称社区版。
商店版需要付费,采用内购终身买断模式,后续我会重点维护和优化。社区版目前还可以使用,不过后续不会再维护。
<img src="https://cdn.ripperhe.com/oss/master/2022/0709/bob-category.jpg" alt="Bob 版本区分" width=800>
## 商店版官网
Bob 商店版官网地址为 <https://bobtranslate.com>
## 社区版官网
Bob 社区版官网地址为 <https://v0.bobtranslate.com>
如果您是通过 Bob 社区版 App 跳转到当前页面,说明您的 Bob 社区版 App 不是最新版本。请将 Bob 社区版 App 升级到社区版最新版本,升级之后 App 内的所有链接都会跳转到 Bob 社区版新的官网地址。
================================================
FILE: docs/_media/.gitkeep
================================================
================================================
FILE: docs/_src/docsify/docsify-copy-code.js
================================================
/*!
* docsify-copy-code
* v2.1.1
* https://github.com/jperasmus/docsify-copy-code
* (c) 2017-2020 JP Erasmus <jperasmus11@gmail.com>
* MIT license
*/
!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||[])}();
//# sourceMappingURL=docsify-copy-code.min.js.map
================================================
FILE: docs/_src/docsify/search.js
================================================
(function () {
/**
* Converts a colon formatted string to a object with properties.
*
* This is process a provided string and look for any tokens in the format
* of `:name[=value]` and then convert it to a object and return.
* An example of this is ':include :type=code :fragment=demo' is taken and
* then converted to:
*
* ```
* {
* include: '',
* type: 'code',
* fragment: 'demo'
* }
* ```
*
* @param {string} str The string to parse.
*
* @return {object} The original string and parsed object, { str, config }.
*/
function getAndRemoveConfig(str) {
if ( str === void 0 ) str = '';
var config = {};
if (str) {
str = str
.replace(/^('|")/, '')
.replace(/('|")$/, '')
.replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g, function (m, key, value) {
if (key.indexOf(':') === -1) {
config[key] = (value && value.replace(/"/g, '')) || true;
return '';
}
return m;
})
.trim();
}
return { str: str, config: config };
}
/* eslint-disable no-unused-vars */
var INDEXS = {};
var LOCAL_STORAGE = {
EXPIRE_KEY: 'docsify.search.expires',
INDEX_KEY: 'docsify.search.index',
};
function resolveExpireKey(namespace) {
return namespace
? ((LOCAL_STORAGE.EXPIRE_KEY) + "/" + namespace)
: LOCAL_STORAGE.EXPIRE_KEY;
}
function resolveIndexKey(namespace) {
return namespace
? ((LOCAL_STORAGE.INDEX_KEY) + "/" + namespace)
: LOCAL_STORAGE.INDEX_KEY;
}
function escapeHtml(string) {
var entityMap = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
};
return String(string).replace(/[&<>"']/g, function (s) { return entityMap[s]; });
}
function getAllPaths(router) {
var paths = [];
Docsify.dom
.findAll('.sidebar-nav a:not(.section-link):not([data-nosearch])')
.forEach(function (node) {
var href = node.href;
var originHref = node.getAttribute('href');
var path = router.parse(href).path;
if (
path &&
paths.indexOf(path) === -1 &&
!Docsify.util.isAbsolutePath(originHref)
) {
paths.push(path);
}
});
return paths;
}
function getTableData(token) {
if (!token.text && token.type === 'table') {
token.cells.unshift(token.header);
token.text = token.cells
.map(function(rows) {
return rows.join(' | ');
})
.join(' |\n ');
}
return token.text;
}
function getListData(token) {
if (!token.text && token.type === 'list') {
token.text = token.raw;
}
return token.text;
}
function saveData(maxAge, expireKey, indexKey) {
localStorage.setItem(expireKey, Date.now() + maxAge);
localStorage.setItem(indexKey, JSON.stringify(INDEXS));
}
function genIndex(path, content, router, depth) {
if ( content === void 0 ) content = '';
var tokens = window.marked.lexer(content);
var slugify = window.Docsify.slugify;
var index = {};
var slug;
var title = '';
tokens.forEach(function(token, tokenIndex) {
if (token.type === 'heading' && token.depth <= depth) {
var ref = getAndRemoveConfig(token.text);
var str = ref.str;
var config = ref.config;
if (config.id) {
slug = router.toURL(path, { id: slugify(config.id) });
} else {
slug = router.toURL(path, { id: slugify(escapeHtml(token.text)) });
}
if (str) {
title = str
.replace(/<!-- {docsify-ignore} -->/, '')
.replace(/{docsify-ignore}/, '')
.replace(/<!-- {docsify-ignore-all} -->/, '')
.replace(/{docsify-ignore-all}/, '')
.trim();
}
index[slug] = { slug: slug, title: title, body: '' };
} else {
if (tokenIndex === 0) {
slug = router.toURL(path);
index[slug] = {
slug: slug,
title: path !== '/' ? path.slice(1) : 'Home Page',
body: token.text || '',
};
}
if (!slug) {
return;
}
if (!index[slug]) {
index[slug] = { slug: slug, title: '', body: '' };
} else if (index[slug].body) {
token.text = getTableData(token);
token.text = getListData(token);
index[slug].body += '\n' + (token.text || '');
} else {
token.text = getTableData(token);
token.text = getListData(token);
index[slug].body = index[slug].body
? index[slug].body + token.text
: token.text;
}
}
});
slugify.clear();
return index;
}
function ignoreDiacriticalMarks(keyword) {
if (keyword && keyword.normalize) {
return keyword.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
}
return keyword;
}
/**
* @param {String} query Search query
* @returns {Array} Array of results
*/
function search(query) {
var matchingResults = [];
var data = [];
Object.keys(INDEXS).forEach(function (key) {
data = data.concat(Object.keys(INDEXS[key]).map(function (page) { return INDEXS[key][page]; }));
});
query = query.trim();
var keywords = query.split(/[\s\-,\\/]+/);
if (keywords.length !== 1) {
keywords = [].concat(query, keywords);
}
var loop = function ( i ) {
var post = data[i];
var matchesScore = 0;
var resultStr = '';
var handlePostTitle = '';
var handlePostContent = '';
var postTitle = post.title && post.title.trim();
var postContent = post.body && post.body.trim();
var postUrl = post.slug || '';
if (postTitle) {
keywords.forEach(function (keyword) {
// From https://github.com/sindresorhus/escape-string-regexp
var regEx = new RegExp(
escapeHtml(ignoreDiacriticalMarks(keyword)).replace(
/[|\\{}()[\]^$+*?.]/g,
'\\$&'
),
'gi'
);
var indexTitle = -1;
var indexContent = -1;
handlePostTitle = postTitle
? escapeHtml(ignoreDiacriticalMarks(postTitle))
: postTitle;
handlePostContent = postContent
? escapeHtml(ignoreDiacriticalMarks(postContent))
: postContent;
indexTitle = postTitle ? handlePostTitle.search(regEx) : -1;
indexContent = postContent ? handlePostContent.search(regEx) : -1;
if (indexTitle >= 0 || indexContent >= 0) {
matchesScore += indexTitle >= 0 ? 3 : indexContent >= 0 ? 2 : 0;
if (indexContent < 0) {
indexContent = 0;
}
var start = 0;
var end = 0;
start = indexContent < 11 ? 0 : indexContent - 10;
end = start === 0 ? 70 : indexContent + keyword.length + 60;
if (postContent && end > postContent.length) {
end = postContent.length;
}
var matchContent =
'...' +
handlePostContent
.substring(start, end)
.replace(
regEx,
function (word) { return ("<em class=\"search-keyword\">" + word + "</em>"); }
) +
'...';
resultStr += matchContent;
}
});
if (matchesScore > 0) {
var matchingPost = {
title: handlePostTitle,
content: postContent ? resultStr : '',
url: postUrl,
score: matchesScore,
};
matchingResults.push(matchingPost);
}
}
};
for (var i = 0; i < data.length; i++) loop( i );
return matchingResults.sort(function (r1, r2) { return r2.score - r1.score; });
}
function init(config, vm) {
var isAuto = config.paths === 'auto';
var paths = isAuto ? getAllPaths(vm.router) : config.paths;
var namespaceSuffix = '';
// only in auto mode
if (paths.length && isAuto && config.pathNamespaces) {
var path = paths[0];
if (Array.isArray(config.pathNamespaces)) {
namespaceSuffix =
config.pathNamespaces.filter(
function (prefix) { return path.slice(0, prefix.length) === prefix; }
)[0] || namespaceSuffix;
} else if (config.pathNamespaces instanceof RegExp) {
var matches = path.match(config.pathNamespaces);
if (matches) {
namespaceSuffix = matches[0];
}
}
var isExistHome = paths.indexOf(namespaceSuffix + '/') === -1;
var isExistReadme = paths.indexOf(namespaceSuffix + '/README') === -1;
if (isExistHome && isExistReadme) {
paths.unshift(namespaceSuffix + '/');
}
} else if (paths.indexOf('/') === -1 && paths.indexOf('/README') === -1) {
paths.unshift('/');
}
var expireKey = resolveExpireKey(config.namespace) + namespaceSuffix;
var indexKey = resolveIndexKey(config.namespace) + namespaceSuffix;
var isExpired = localStorage.getItem(expireKey) < Date.now();
INDEXS = JSON.parse(localStorage.getItem(indexKey));
if (isExpired) {
INDEXS = {};
} else if (!isAuto) {
return;
}
var len = paths.length;
var count = 0;
paths.forEach(function (path) {
if (INDEXS[path]) {
return count++;
}
Docsify.get(vm.router.getFile(path), false, vm.config.requestHeaders).then(
function (result) {
INDEXS[path] = genIndex(path, result, vm.router, config.depth);
len === ++count && saveData(config.maxAge, expireKey, indexKey);
}
);
});
}
/* eslint-disable no-unused-vars */
var NO_DATA_TEXT = '';
var options;
function style() {
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}";
Docsify.dom.style(code);
}
function tpl(defaultValue) {
if ( defaultValue === void 0 ) defaultValue = '';
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>";
var el = Docsify.dom.create('div', html);
var aside = Docsify.dom.find('aside');
Docsify.dom.toggleClass(el, 'search');
Docsify.dom.before(aside, el);
}
function doSearch(value) {
var $search = Docsify.dom.find('div.search');
var $panel = Docsify.dom.find($search, '.results-panel');
var $clearBtn = Docsify.dom.find($search, '.clear-button');
var $sidebarNav = Docsify.dom.find('.sidebar-nav');
var $appName = Docsify.dom.find('.app-name');
if (!value) {
$panel.classList.remove('show');
$clearBtn.classList.remove('show');
$panel.innerHTML = '';
if (options.hideOtherSidebarContent) {
$sidebarNav && $sidebarNav.classList.remove('hide');
$appName && $appName.classList.remove('hide');
}
return;
}
var matchs = search(value);
var html = '';
matchs.forEach(function (post) {
html += "<div class=\"matching-post\">\n<a href=\"" + (post.url) + "\">\n<h2>" + (post.title) + "</h2>\n<p>" + (post.content) + "</p>\n</a>\n</div>";
});
$panel.classList.add('show');
$clearBtn.classList.add('show');
$panel.innerHTML = html || ("<p class=\"empty\">" + NO_DATA_TEXT + "</p>");
if (options.hideOtherSidebarContent) {
$sidebarNav && $sidebarNav.classList.add('hide');
$appName && $appName.classList.add('hide');
}
}
function bindEvents() {
var $search = Docsify.dom.find('div.search');
var $input = Docsify.dom.find($search, 'input');
var $inputWrap = Docsify.dom.find($search, '.input-wrap');
var timeId;
/**
Prevent to Fold sidebar.
When searching on the mobile end,
the sidebar is collapsed when you click the INPUT box,
making it impossible to search.
*/
Docsify.dom.on(
$search,
'click',
function (e) { return ['A', 'H2', 'P', 'EM'].indexOf(e.target.tagName) === -1 &&
e.stopPropagation(); }
);
Docsify.dom.on($input, 'input', function (e) {
clearTimeout(timeId);
timeId = setTimeout(function (_) { return doSearch(e.target.value.trim()); }, 100);
});
Docsify.dom.on($inputWrap, 'click', function (e) {
// Click input outside
if (e.target.tagName !== 'INPUT') {
$input.value = '';
doSearch();
}
});
}
function updatePlaceholder(text, path) {
var $input = Docsify.dom.getNode('.search input[type="search"]');
if (!$input) {
return;
}
if (typeof text === 'string') {
$input.placeholder = text;
} else {
var match = Object.keys(text).filter(function (key) { return path.indexOf(key) > -1; })[0];
$input.placeholder = text[match];
}
}
function updateNoData(text, path) {
if (typeof text === 'string') {
NO_DATA_TEXT = text;
} else {
var match = Object.keys(text).filter(function (key) { return path.indexOf(key) > -1; })[0];
NO_DATA_TEXT = text[match];
}
}
function updateOptions(opts) {
options = opts;
}
function init$1(opts, vm) {
var keywords = vm.router.parse().query.s;
updateOptions(opts);
style();
tpl(keywords);
bindEvents();
keywords && setTimeout(function (_) { return doSearch(keywords); }, 500);
}
function update(opts, vm) {
updateOptions(opts);
updatePlaceholder(opts.placeholder, vm.route.path);
updateNoData(opts.noData, vm.route.path);
}
/* eslint-disable no-unused-vars */
var CONFIG = {
placeholder: 'Type to search',
noData: 'No Results!',
paths: 'auto',
depth: 2,
maxAge: 86400000, // 1 day
hideOtherSidebarContent: false,
namespace: undefined,
pathNamespaces: undefined,
};
var install = function(hook, vm) {
var util = Docsify.util;
var opts = vm.config.search || CONFIG;
if (Array.isArray(opts)) {
CONFIG.paths = opts;
} else if (typeof opts === 'object') {
CONFIG.paths = Array.isArray(opts.paths) ? opts.paths : 'auto';
CONFIG.maxAge = util.isPrimitive(opts.maxAge) ? opts.maxAge : CONFIG.maxAge;
CONFIG.placeholder = opts.placeholder || CONFIG.placeholder;
CONFIG.noData = opts.noData || CONFIG.noData;
CONFIG.depth = opts.depth || CONFIG.depth;
CONFIG.hideOtherSidebarContent =
opts.hideOtherSidebarContent || CONFIG.hideOtherSidebarContent;
CONFIG.namespace = opts.namespace || CONFIG.namespace;
CONFIG.pathNamespaces = opts.pathNamespaces || CONFIG.pathNamespaces;
}
var isAuto = CONFIG.paths === 'auto';
hook.mounted(function (_) {
init$1(CONFIG, vm);
!isAuto && init(CONFIG, vm);
});
hook.doneEach(function (_) {
update(CONFIG, vm);
isAuto && init(CONFIG, vm);
});
};
$docsify.plugins = [].concat(install, $docsify.plugins);
}());
================================================
FILE: docs/_src/docsify/vue.css
================================================
@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}
================================================
FILE: docs/_src/docsify/zoom-image.js
================================================
(function () {
/*! medium-zoom 1.0.6 | MIT License | https://github.com/francoischalifour/medium-zoom */
var _extends = Object.assign || function (target) {
var arguments$1 = arguments;
for (var i = 1; i < arguments.length; i++) {
var source = arguments$1[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
var isSupported = function isSupported(node) {
return node.tagName === 'IMG';
};
/* eslint-disable-next-line no-prototype-builtins */
var isNodeList = function isNodeList(selector) {
return NodeList.prototype.isPrototypeOf(selector);
};
var isNode = function isNode(selector) {
return selector && selector.nodeType === 1;
};
var isSvg = function isSvg(image) {
var source = image.currentSrc || image.src;
return source.substr(-4).toLowerCase() === '.svg';
};
var getImagesFromSelector = function getImagesFromSelector(selector) {
try {
if (Array.isArray(selector)) {
return selector.filter(isSupported);
}
if (isNodeList(selector)) {
// Do not use spread operator or Array.from() for IE support
return [].slice.call(selector).filter(isSupported);
}
if (isNode(selector)) {
return [selector].filter(isSupported);
}
if (typeof selector === 'string') {
// Do not use spread operator or Array.from() for IE support
return [].slice.call(document.querySelectorAll(selector)).filter(isSupported);
}
return [];
} catch (err) {
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');
}
};
var createOverlay = function createOverlay(background) {
var overlay = document.createElement('div');
overlay.classList.add('medium-zoom-overlay');
overlay.style.background = background;
return overlay;
};
var cloneTarget = function cloneTarget(template) {
var _template$getBounding = template.getBoundingClientRect(),
top = _template$getBounding.top,
left = _template$getBounding.left,
width = _template$getBounding.width,
height = _template$getBounding.height;
var clone = template.cloneNode();
var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0;
clone.removeAttribute('id');
clone.style.position = 'absolute';
clone.style.top = top + scrollTop + 'px';
clone.style.left = left + scrollLeft + 'px';
clone.style.width = width + 'px';
clone.style.height = height + 'px';
clone.style.transform = '';
return clone;
};
var createCustomEvent = function createCustomEvent(type, params) {
var eventParams = _extends({
bubbles: false,
cancelable: false,
detail: undefined
}, params);
if (typeof window.CustomEvent === 'function') {
return new CustomEvent(type, eventParams);
}
var customEvent = document.createEvent('CustomEvent');
customEvent.initCustomEvent(type, eventParams.bubbles, eventParams.cancelable, eventParams.detail);
return customEvent;
};
var mediumZoom = function mediumZoom(selector) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
/**
* Ensure the compatibility with IE11 if no Promise polyfill are used.
*/
var Promise = window.Promise || function Promise(fn) {
function noop() {}
fn(noop, noop);
};
var _handleClick = function _handleClick(event) {
var target = event.target;
if (target === overlay) {
close();
return;
}
if (images.indexOf(target) === -1) {
return;
}
toggle({ target: target });
};
var _handleScroll = function _handleScroll() {
if (isAnimating || !active.original) {
return;
}
var currentScroll = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
if (Math.abs(scrollTop - currentScroll) > zoomOptions.scrollOffset) {
setTimeout(close, 150);
}
};
var _handleKeyUp = function _handleKeyUp(event) {
var key = event.key || event.keyCode;
// Close if escape key is pressed
if (key === 'Escape' || key === 'Esc' || key === 27) {
close();
}
};
var update = function update() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var newOptions = options;
if (options.background) {
overlay.style.background = options.background;
}
if (options.container && options.container instanceof Object) {
newOptions.container = _extends({}, zoomOptions.container, options.container);
}
if (options.template) {
var template = isNode(options.template) ? options.template : document.querySelector(options.template);
newOptions.template = template;
}
zoomOptions = _extends({}, zoomOptions, newOptions);
images.forEach(function (image) {
image.dispatchEvent(createCustomEvent('medium-zoom:update', {
detail: { zoom: zoom }
}));
});
return zoom;
};
var clone = function clone() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
return mediumZoom(_extends({}, zoomOptions, options));
};
var attach = function attach() {
var arguments$1 = arguments;
for (var _len = arguments.length, selectors = Array(_len), _key = 0; _key < _len; _key++) {
selectors[_key] = arguments$1[_key];
}
var newImages = selectors.reduce(function (imagesAccumulator, currentSelector) {
return [].concat(imagesAccumulator, getImagesFromSelector(currentSelector));
}, []);
newImages.filter(function (newImage) {
return images.indexOf(newImage) === -1;
}).forEach(function (newImage) {
images.push(newImage);
newImage.classList.add('medium-zoom-image');
});
eventListeners.forEach(function (_ref) {
var type = _ref.type,
listener = _ref.listener,
options = _ref.options;
newImages.forEach(function (image) {
image.addEventListener(type, listener, options);
});
});
return zoom;
};
var detach = function detach() {
var arguments$1 = arguments;
for (var _len2 = arguments.length, selectors = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
selectors[_key2] = arguments$1[_key2];
}
if (active.zoomed) {
close();
}
var imagesToDetach = selectors.length > 0 ? selectors.reduce(function (imagesAccumulator, currentSelector) {
return [].concat(imagesAccumulator, getImagesFromSelector(currentSelector));
}, []) : images;
imagesToDetach.forEach(function (image) {
image.classList.remove('medium-zoom-image');
image.dispatchEvent(createCustomEvent('medium-zoom:detach', {
detail: { zoom: zoom }
}));
});
images = images.filter(function (image) {
return imagesToDetach.indexOf(image) === -1;
});
return zoom;
};
var on = function on(type, listener) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
images.forEach(function (image) {
image.addEventListener('medium-zoom:' + type, listener, options);
});
eventListeners.push({ type: 'medium-zoom:' + type, listener: listener, options: options });
return zoom;
};
var off = function off(type, listener) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
images.forEach(function (image) {
image.removeEventListener('medium-zoom:' + type, listener, options);
});
eventListeners = eventListeners.filter(function (eventListener) {
return !(eventListener.type === 'medium-zoom:' + type && eventListener.listener.toString() === listener.toString());
});
return zoom;
};
var open = function open() {
var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
target = _ref2.target;
var _animate = function _animate() {
var container = {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
left: 0,
top: 0,
right: 0,
bottom: 0
};
var viewportWidth = void 0;
var viewportHeight = void 0;
if (zoomOptions.container) {
if (zoomOptions.container instanceof Object) {
// The container is given as an object with properties like width, height, left, top
container = _extends({}, container, zoomOptions.container);
// We need to adjust custom options like container.right or container.bottom
viewportWidth = container.width - container.left - container.right - zoomOptions.margin * 2;
viewportHeight = container.height - container.top - container.bottom - zoomOptions.margin * 2;
} else {
// The container is given as an element
var zoomContainer = isNode(zoomOptions.container) ? zoomOptions.container : document.querySelector(zoomOptions.container);
var _zoomContainer$getBou = zoomContainer.getBoundingClientRect(),
_width = _zoomContainer$getBou.width,
_height = _zoomContainer$getBou.height,
_left = _zoomContainer$getBou.left,
_top = _zoomContainer$getBou.top;
container = _extends({}, container, {
width: _width,
height: _height,
left: _left,
top: _top
});
}
}
viewportWidth = viewportWidth || container.width - zoomOptions.margin * 2;
viewportHeight = viewportHeight || container.height - zoomOptions.margin * 2;
var zoomTarget = active.zoomedHd || active.original;
var naturalWidth = isSvg(zoomTarget) ? viewportWidth : zoomTarget.naturalWidth || viewportWidth;
var naturalHeight = isSvg(zoomTarget) ? viewportHeight : zoomTarget.naturalHeight || viewportHeight;
var _zoomTarget$getBoundi = zoomTarget.getBoundingClientRect(),
top = _zoomTarget$getBoundi.top,
left = _zoomTarget$getBoundi.left,
width = _zoomTarget$getBoundi.width,
height = _zoomTarget$getBoundi.height;
var scaleX = Math.min(naturalWidth, viewportWidth) / width;
var scaleY = Math.min(naturalHeight, viewportHeight) / height;
var scale = Math.min(scaleX, scaleY);
var translateX = (-left + (viewportWidth - width) / 2 + zoomOptions.margin + container.left) / scale;
var translateY = (-top + (viewportHeight - height) / 2 + zoomOptions.margin + container.top) / scale;
var transform = 'scale(' + scale + ') translate3d(' + translateX + 'px, ' + translateY + 'px, 0)';
active.zoomed.style.transform = transform;
if (active.zoomedHd) {
active.zoomedHd.style.transform = transform;
}
};
return new Promise(function (resolve) {
if (target && images.indexOf(target) === -1) {
resolve(zoom);
return;
}
var _handleOpenEnd = function _handleOpenEnd() {
isAnimating = false;
active.zoomed.removeEventListener('transitionend', _handleOpenEnd);
active.original.dispatchEvent(createCustomEvent('medium-zoom:opened', {
detail: { zoom: zoom }
}));
resolve(zoom);
};
if (active.zoomed) {
resolve(zoom);
return;
}
if (target) {
// The zoom was triggered manually via a click
active.original = target;
} else if (images.length > 0) {
var _images = images;
active.original = _images[0];
} else {
resolve(zoom);
return;
}
active.original.dispatchEvent(createCustomEvent('medium-zoom:open', {
detail: { zoom: zoom }
}));
scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
isAnimating = true;
active.zoomed = cloneTarget(active.original);
document.body.appendChild(overlay);
if (zoomOptions.template) {
var template = isNode(zoomOptions.template) ? zoomOptions.template : document.querySelector(zoomOptions.template);
active.template = document.createElement('div');
active.template.appendChild(template.content.cloneNode(true));
document.body.appendChild(active.template);
}
document.body.appendChild(active.zoomed);
window.requestAnimationFrame(function () {
document.body.classList.add('medium-zoom--opened');
});
active.original.classList.add('medium-zoom-image--hidden');
active.zoomed.classList.add('medium-zoom-image--opened');
active.zoomed.addEventListener('click', close);
active.zoomed.addEventListener('transitionend', _handleOpenEnd);
if (active.original.getAttribute('data-zoom-src')) {
active.zoomedHd = active.zoomed.cloneNode();
// Reset the `scrset` property or the HD image won't load.
active.zoomedHd.removeAttribute('srcset');
active.zoomedHd.removeAttribute('sizes');
active.zoomedHd.src = active.zoomed.getAttribute('data-zoom-src');
active.zoomedHd.onerror = function () {
clearInterval(getZoomTargetSize);
console.warn('Unable to reach the zoom image target ' + active.zoomedHd.src);
active.zoomedHd = null;
_animate();
};
// We need to access the natural size of the full HD
// target as fast as possible to compute the animation.
var getZoomTargetSize = setInterval(function () {
if ( active.zoomedHd.complete) {
clearInterval(getZoomTargetSize);
active.zoomedHd.classList.add('medium-zoom-image--opened');
active.zoomedHd.addEventListener('click', close);
document.body.appendChild(active.zoomedHd);
_animate();
}
}, 10);
} else if (active.original.hasAttribute('srcset')) {
// If an image has a `srcset` attribuet, we don't know the dimensions of the
// zoomed (HD) image (like when `data-zoom-src` is specified).
// Therefore the approach is quite similar.
active.zoomedHd = active.zoomed.cloneNode();
// Resetting the sizes attribute tells the browser to load the
// image best fitting the current viewport size, respecting the `srcset`.
active.zoomedHd.removeAttribute('sizes');
// In Firefox, the `loading` attribute needs to be set to `eager` (default
// value) for the load event to be fired.
active.zoomedHd.removeAttribute('loading');
// Wait for the load event of the hd image. This will fire if the image
// is already cached.
var loadEventListener = active.zoomedHd.addEventListener('load', function () {
active.zoomedHd.removeEventListener('load', loadEventListener);
active.zoomedHd.classList.add('medium-zoom-image--opened');
active.zoomedHd.addEventListener('click', close);
document.body.appendChild(active.zoomedHd);
_animate();
});
} else {
_animate();
}
});
};
var close = function close() {
return new Promise(function (resolve) {
if (isAnimating || !active.original) {
resolve(zoom);
return;
}
var _handleCloseEnd = function _handleCloseEnd() {
active.original.classList.remove('medium-zoom-image--hidden');
document.body.removeChild(active.zoomed);
if (active.zoomedHd) {
document.body.removeChild(active.zoomedHd);
}
document.body.removeChild(overlay);
active.zoomed.classList.remove('medium-zoom-image--opened');
if (active.template) {
document.body.removeChild(active.template);
}
isAnimating = false;
active.zoomed.removeEventListener('transitionend', _handleCloseEnd);
active.original.dispatchEvent(createCustomEvent('medium-zoom:closed', {
detail: { zoom: zoom }
}));
active.original = null;
active.zoomed = null;
active.zoomedHd = null;
active.template = null;
resolve(zoom);
};
isAnimating = true;
document.body.classList.remove('medium-zoom--opened');
active.zoomed.style.transform = '';
if (active.zoomedHd) {
active.zoomedHd.style.transform = '';
}
// Fade out the template so it's not too abrupt
if (active.template) {
active.template.style.transition = 'opacity 150ms';
active.template.style.opacity = 0;
}
active.original.dispatchEvent(createCustomEvent('medium-zoom:close', {
detail: { zoom: zoom }
}));
active.zoomed.addEventListener('transitionend', _handleCloseEnd);
});
};
var toggle = function toggle() {
var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
target = _ref3.target;
if (active.original) {
return close();
}
return open({ target: target });
};
var getOptions = function getOptions() {
return zoomOptions;
};
var getImages = function getImages() {
return images;
};
var getZoomedImage = function getZoomedImage() {
return active.original;
};
var images = [];
var eventListeners = [];
var isAnimating = false;
var scrollTop = 0;
var zoomOptions = options;
var active = {
original: null,
zoomed: null,
zoomedHd: null,
template: null
// If the selector is omitted, it's replaced by the options
};if (Object.prototype.toString.call(selector) === '[object Object]') {
zoomOptions = selector;
} else if (selector || typeof selector === 'string' // to process empty string as a selector
) {
attach(selector);
}
// Apply the default option values
zoomOptions = _extends({
margin: 0,
background: '#fff',
scrollOffset: 40,
container: null,
template: null
}, zoomOptions);
var overlay = createOverlay(zoomOptions.background);
document.addEventListener('click', _handleClick);
document.addEventListener('keyup', _handleKeyUp);
document.addEventListener('scroll', _handleScroll);
window.addEventListener('resize', close);
var zoom = {
open: open,
close: close,
toggle: toggle,
update: update,
clone: clone,
attach: attach,
detach: detach,
on: on,
off: off,
getOptions: getOptions,
getImages: getImages,
getZoomedImage: getZoomedImage
};
return zoom;
};
function styleInject(css, ref) {
if ( ref === void 0 ) { ref = {}; }
var insertAt = ref.insertAt;
if (!css || typeof document === 'undefined') { return; }
var head = document.head || document.getElementsByTagName('head')[0];
var style = document.createElement('style');
style.type = 'text/css';
if (insertAt === 'top') {
if (head.firstChild) {
head.insertBefore(style, head.firstChild);
} else {
head.appendChild(style);
}
} else {
head.appendChild(style);
}
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
}
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}";
styleInject(css);
/* eslint-disable no-unused-vars */
var matchesSelector =
Element.prototype.matches ||
Element.prototype.webkitMatchesSelector ||
Element.prototype.msMatchesSelector;
function install(hook) {
var zoom;
hook.doneEach(function (_) {
var elms = Array.apply(
null,
document.querySelectorAll(
'.markdown-section img:not(.emoji):not([data-no-zoom])'
)
);
elms = elms.filter(function (elm) { return matchesSelector.call(elm, 'a img') === false; });
if (zoom) {
zoom.detach();
}
zoom = mediumZoom(elms);
});
}
$docsify.plugins = [].concat(install, $docsify.plugins);
}());
================================================
FILE: docs/_src/source.txt
================================================
https://cdn.jsdelivr.net/npm/docsify/lib/themes/vue.css
https://cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js
https://cdn.jsdelivr.net/npm/docsify/lib/plugins/search.js
https://cdn.jsdelivr.net/npm/docsify/lib/plugins/zoom-image.js
https://cdn.jsdelivr.net/npm/docsify-copy-code
https://cdn.jsdelivr.net/npm/prismjs/components/prism-javascript.min.js
https://cdn.jsdelivr.net/npm/prismjs/components/prism-json.min.js
https://cdn.jsdelivr.net/npm/prismjs/components/prism-applescript.min.js
https://cdn.jsdelivr.net/npm/prismjs/components/prism-bash.min.js
================================================
FILE: docs/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- 网站标题 -->
<title>Bob</title>
<!-- 网站icon -->
<link rel="icon" href="_media/favicon.ico">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Description">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<!-- Theme 主题设置 [vue.css] [buble.css] [dark.css] [pure.css] -->
<link rel="stylesheet" href="_src/docsify/vue.css">
</head>
<body>
<!-- loading显示的文字 -->
<div id="app">Loading...</div>
<script>
window.$docsify = {
// ************************* Configuration *************************
// https://docsify.js.org/#/zh-cn/configuration
// --> 首页 「README.md」
homepage: 'README.md',
// --> 封面 「_coverpage.md」
coverpage: false,
// --> 侧边栏 [_sidebar.md]
loadSidebar: false,
// 说实话,没搞懂
maxLevel: 4,
// 设置侧边栏中,当前文章的最大目录层级;不设置,则不会显示当前文章目录
subMaxLevel: 2,
// --> 导航栏 [_navbar.md]
loadNavbar: false,
// 小屏设备下合并导航栏到侧边栏
mergeNavbar: true,
// --> 404页面 「_404.md」
notFoundPage: "README.md",
// 隐藏侧边栏
hideSidebar: true,
// 网址logo
// logo: '/_media/icon.svg',
// 文档标题,显示在侧边栏顶部
name: 'Bob',
// 仓库地址(设置之后,会在页面右上角渲染一个 GitHub Corner 挂件)
// repo: 'ripperhe/Bob',
// 页面切换之后,自动跳转到顶部
auto2top: true,
// 格式化
formatUpdated: '{MM}/{DD} {HH}:{mm}',
// ************************* Plugins *************************
// https://docsify.js.org/#/zh-cn/plugins
// 全文搜索 - Search
search: {
placeholder: '搜索',
noData: '找不到结果',
},
plugins: [
// Edit On Github
function (hook, vm) {
hook.beforeEach(function (html) {
var chatHtml = '[:rocket: 点击此处联系我](https://bobtranslate.com/general/contact.html)'
return html
+ '\n\n----\n\n'
+ chatHtml
})
},
]
}
</script>
<script src="_src/docsify/docsify.min.js"></script>
<!-- ************************* Plugins ************************* -->
<!-- Plugins:全文搜索 -->
<script src="_src/docsify/search.js"></script>
<!-- Plugins:图片缩放 - Zoom image -->
<script src="_src/docsify/zoom-image.js"></script>
<!-- 点击代码块右上角复制代码 -->
<script src="_src/docsify/docsify-copy-code.js"></script>
<!-- Plugins:代码高亮 prism https://github.com/PrismJS/prism -->
<script src="_src/prismjs/prism-javascript.min.js"></script>
<script src="_src/prismjs/prism-json.min.js"></script>
<script src="_src/prismjs/prism-applescript.min.js"></script>
<script src="_src/prismjs/prism-bash.min.js"></script>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-4EK44EJ8FX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', 'G-4EK44EJ8FX');
</script>
</body>
</html>
gitextract_06josyrd/
├── .github/
│ └── ISSUE_TEMPLATE/
│ └── config.yml
├── .gitignore
├── README.md
├── appcast.xml
└── docs/
├── .nojekyll
├── README.md
├── _media/
│ └── .gitkeep
├── _src/
│ ├── docsify/
│ │ ├── docsify-copy-code.js
│ │ ├── search.js
│ │ ├── vue.css
│ │ └── zoom-image.js
│ └── source.txt
└── index.html
SYMBOL INDEX (25 symbols across 3 files)
FILE: docs/_src/docsify/docsify-copy-code.js
function s (line 8) | function s(o){return(s="function"==typeof Symbol&&"symbol"==typeof Symbo...
FILE: docs/_src/docsify/search.js
function getAndRemoveConfig (line 22) | function getAndRemoveConfig(str) {
function resolveExpireKey (line 54) | function resolveExpireKey(namespace) {
function resolveIndexKey (line 60) | function resolveIndexKey(namespace) {
function escapeHtml (line 66) | function escapeHtml(string) {
function getAllPaths (line 78) | function getAllPaths(router) {
function getTableData (line 100) | function getTableData(token) {
function getListData (line 112) | function getListData(token) {
function saveData (line 119) | function saveData(maxAge, expireKey, indexKey) {
function genIndex (line 124) | function genIndex(path, content, router, depth) {
function ignoreDiacriticalMarks (line 190) | function ignoreDiacriticalMarks(keyword) {
function search (line 201) | function search(query) {
function init (line 294) | function init(config, vm) {
function style (line 360) | function style() {
function tpl (line 366) | function tpl(defaultValue) {
function doSearch (line 377) | function doSearch(value) {
function bindEvents (line 413) | function bindEvents() {
function updatePlaceholder (line 446) | function updatePlaceholder(text, path) {
function updateNoData (line 461) | function updateNoData(text, path) {
function updateOptions (line 470) | function updateOptions(opts) {
function init$1 (line 474) | function init$1(opts, vm) {
function update (line 484) | function update(opts, vm) {
FILE: docs/_src/docsify/zoom-image.js
function noop (line 117) | function noop() {}
function styleInject (line 599) | function styleInject(css, ref) {
function install (line 636) | function install(hook) {
Condensed preview — 13 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (85K chars).
[
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 164,
"preview": "blank_issues_enabled: false\ncontact_links:\n - name: Bob 技术支持\n url: https://bobtranslate.com/general/contact.html\n "
},
{
"path": ".gitignore",
"chars": 1407,
"preview": "# Xcode\n#\n# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore\n\n"
},
{
"path": "README.md",
"chars": 3864,
"preview": "<p align=\"center\">\n <img src=\"https://cdn.ripperhe.com/oss/master/2019/1222/bob-logo.png\" width=240 />\n</p>\n<p align=\"c"
},
{
"path": "appcast.xml",
"chars": 15129,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<rss xmlns:sparkle=\"http://www.andymatuschak.org/xml-namespaces/"
},
{
"path": "docs/.nojekyll",
"chars": 0,
"preview": ""
},
{
"path": "docs/README.md",
"chars": 686,
"preview": "# Bob 官网迁移通知\n\nBob 官网之前通过 Gitee/GitHub Pages 进行部署,但是经常会有用户无法访问,现已将官网部署到我自己的服务器,域名地址也有所变更。\n\n!> **Bob 已发布到 Mac App Store,详情"
},
{
"path": "docs/_media/.gitkeep",
"chars": 1,
"preview": "\n"
},
{
"path": "docs/_src/docsify/docsify-copy-code.js",
"chars": 3634,
"preview": "/*!\n * docsify-copy-code\n * v2.1.1\n * https://github.com/jperasmus/docsify-copy-code\n * (c) 2017-2020 JP Erasmus <jperas"
},
{
"path": "docs/_src/docsify/search.js",
"chars": 17999,
"preview": "(function () {\n /**\n * Converts a colon formatted string to a object with properties.\n *\n * This is proce"
},
{
"path": "docs/_src/docsify/vue.css",
"chars": 12938,
"preview": "@import url(\"https://fonts.googleapis.com/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600\");*{-webkit-font-smoothing:"
},
{
"path": "docs/_src/docsify/zoom-image.js",
"chars": 22843,
"preview": "(function () {\n /*! medium-zoom 1.0.6 | MIT License | https://github.com/francoischalifour/medium-zoom */\n var _ex"
},
{
"path": "docs/_src/source.txt",
"chars": 558,
"preview": "https://cdn.jsdelivr.net/npm/docsify/lib/themes/vue.css\nhttps://cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js\nhttps://"
},
{
"path": "docs/index.html",
"chars": 3112,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n <meta charset=\"UTF-8\">\n <!-- 网站标题 -->\n <title>Bob</title>\n <!-- 网站icon -->"
}
]
About this extraction
This page contains the full source code of the ripperhe/Bob GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 13 files (80.4 KB), approximately 24.2k tokens, and a symbol index with 25 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.