Merge pull request #1 from TevinLi/master

同步最新版本
This commit is contained in:
mikusjelly 2018-02-08 10:41:37 +08:00 committed by GitHub
commit d6f10f321d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 6235 additions and 1985 deletions

3
.gitignore vendored
View File

@ -51,3 +51,6 @@ Temporary Items
*.project
*.temp
*.log
*.cmd

21
.npmignore Normal file
View File

@ -0,0 +1,21 @@
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
.DS_Store
.AppleDouble
.LSOverride
npm-debug.log
node_modules
*.project
*.temp
*.log
*.cmd

View File

@ -1,4 +1,179 @@
# 版本日志
# amWiki 版本日志
## [v1.2.1](https://github.com/TevinLi/amWiki/releases/tag/v1.2.1)
_2017-08-08_
- 改善 Mac 上点号开头的系统文件带来的诸多问题(例如导出报错等)([#32](https://github.com/TevinLi/amWiki/issues/32)、[#67](https://github.com/TevinLi/amWiki/issues/67))
- 允许在 config 上重新定义 library 路径 ([#47](https://github.com/TevinLi/amWiki/issues/47))
- 文章篇内目录显示优化 ([#33](https://github.com/TevinLi/amWiki/issues/33))
- 文章篇内目录序号识别,已有序号不再增加序号 ([#33](https://github.com/TevinLi/amWiki/issues/33))
- 基础 Markdown 语法扩充,实现文字飘红
- 统一官网和文档中心网址
- 修复几处显示细节
## [v1.2.0](https://github.com/TevinLi/amWiki/releases/tag/v1.2.0)
_2017-07-18_
- 新增本地数据挂载模式,可以直接双击 index 在浏览器中访问整个文库
- Atom 平台和 Node-Npm 平台挂载数据操作绑定
- 本地页面挂载数据生成与嵌入模块
- 页面加载数据,页面打开时立即判断和读取挂载数据
- 改进搜索,网络不可用时不再用 worker 而是直接搜索
- disable 本地模式状态下的接口测试入口
- 隐藏本地模式状态下的重建缓存按钮
- 基础 Markdown 语法扩充,支持 todo list 复选框 ([#56](https://github.com/TevinLi/amWiki/issues/56))
- 终端本地服务器命令新增可关闭 amWiki 索引页的可选参数
- 优化本地服务器容错性
- 搜索面板结果列表点击不再跳转页面
- 导航增加文档文件名转义,文档文件名允许使用语法符号`()[]'"`而不再冲突
- 对带`&`符号的文件名在URL地址上用特殊转义替代不会再被作为参数截断 ([#64](https://github.com/TevinLi/amWiki/issues/64))
- 修复服务器文件系统为GBK编码时深目录无法读取的bug
- 调整大量注释、调整一些变量名,标记私有与公有
## [v1.1.3](https://github.com/TevinLi/amWiki/releases/tag/v1.1.3)
_2017-06-25_
- 增加大图预览模式第一期 ([#44](https://github.com/TevinLi/amWiki/issues/44))
- Markdown 基础语法扩充,支持定义图片大小与对齐方式 ([#45](https://github.com/TevinLi/amWiki/issues/45))
- 允许首页的文件名通过增加 `home-` 前缀来自定义
- 修复图片拖拽丢失扩展名的问题 ([#58](https://github.com/TevinLi/amWiki/issues/58))
- 更改 highlight.js 为完整版,支持 176 种语言语法高亮
- 修复测试面板切换文档时的一个显示问题
## [v1.1.2](https://github.com/TevinLi/amWiki/releases/tag/v1.1.2)
_2017-06-03_
- 允许在 config.json 上使用 imports 将额外的 css 和 js 文件嵌入页面 ([#43](https://github.com/TevinLi/amWiki/issues/43))
- 允许本地 server 多开,端口被占用时累加端口号 ([#46](https://github.com/TevinLi/amWiki/issues/46))
- 新增本地 server 索引页,本地根 url 不再 404
- 修复创建时取消创建操作的一个问题
- 调整一些文案
## [v1.1.1](https://github.com/TevinLi/amWiki/releases/tag/v1.1.1)
_2017-05-21_
- 紧急修复 win 平台 npm 创建报错的问题
## [v1.1.0](https://github.com/TevinLi/amWiki/releases/tag/v1.1.0)
_2017-05-20_
- 实现无限级导航 ([#5](https://github.com/TevinLi/amWiki/issues/5)、[#30](https://github.com/TevinLi/amWiki/pull/30))
- 实现导航水平滚动条,调和水平垂直两滚动条冲突
- 实现 PC、Mobi 端导航栏无限级梯度的层级显示
- 实现目录无限级深度树形与列表数据读取
- 实现无限级导航 Markdown 生成
- 实现无限级重复的排序 ID 检查
- 实现无限级图片粘贴、文件拖拽
- 优化工作端查找 library 文件夹
- 实现拖拽文件文件名汉字转拼音
- 修复导航初始化时展开的一个问题
---
### v1.0.6
_2017-05-05_
- 修复未开启测试模块的文库 web 端无内容显示的问题
- 修复导航未展开目录时显示不全只有一边的问题
- 修复上一版本 npm 失误的发布造成 Linux 平台命令无效的问题
- 移动端点击搜索按钮时关闭导航层 ([#40](https://github.com/TevinLi/amWiki/issues/40))
### v1.0.5
_2017-04-28_
- 导航筛选功能增强
- 文件夹匹配
- 筛选多个词
- 忽略筛选词大小写
- 筛选条件非主动删除不消失
- 使用递归进行筛选,为无限级做准备 ([#5](https://github.com/TevinLi/amWiki/issues/5))
- 修复web端在移动端点击导航导航不自动收起的问题 ([#40](https://github.com/TevinLi/amWiki/issues/40))
- 修复web端测试面板在不关闭的条件下跳转文档后的一个显示问题
- 菜单栏增加 `从剪切板粘贴截图` 项,调和与其他插件快捷键冲突 ([#41](https://github.com/TevinLi/amWiki/issues/41))
### v1.0.4
_2017-04-22_
- 紧急修复命令行本地服务器不能启动的问题
### v1.0.3
_2017-04-22_
- 基础 Markdown 语法扩展,支持 footnote 脚注 ([#37](https://github.com/TevinLi/amWiki/issues/37))
- 修复 Windows 平台下 gitbash 执行命令结束报错的问题
- 修复 Linux 平台下 `/usr/bin/env` 解释器调用失败的问题
- 修复 Linux 平台下 create、version 报错的问题
- 释放快捷键绑定 Ctrl+D、Ctrl+H ([#38](https://github.com/TevinLi/amWiki/issues/38))
- 取消 readme 单篇长文介绍,仅保留安装方法,详细介绍引导至文档中心
### v1.0.2
_2017-04-04_
- 命令行启动服务器允许使用自定义端口参数
- 截图粘贴增强QQ聊天、历史记录面板可复制单张图片直接粘入文档
- 修复命令行板块创建文库操作 $navigation.md 缺失的问题
- 修复本地 http 服务器 500 页面崩溃的问题
- 修复 web 端打开带 hash 地址链接时hash 无效的问题
- 修复命令行显示版本号报错问题
### v1.0.1
_2017-03-30_
- 修复 npm 渠道安装失败的问题
- readme 添加 npm icon
### v1.0.0
_2017-03-30_
- [ _祝贺 amWiki 项目一周岁_ ]
- 构建基于 npm 的**命令行板块**,使用命令行可以同样创建维护文库
- 重构原工作端,分离为**构建板块**与**编辑器板块**
- 引入 ES6 异步编程,使用 Generator、yield、Thunk将编辑器板块的 confirm 转为 confirm2并与命令行板块的 confirm2 调用形式统一化
- 使用 ES6 异步编程,对构建板块部分回调方法进行重构,优先使用 return 与异步编程而不是 callback
- 使用 const/let 变量、解构赋值、箭头函数、Class 等 ES6 语法润色原工作端代码
- 增加命令行、终端界面信息打印着色、帮助菜单、版本显示
- 规范化部分公共方法注释
---
### v0.8.0
_2016-12-30_
- Web 端页面左右分栏滚动 ([#26](https://github.com/TevinLi/amWiki/issues/26))
- 工作端解除部分判断对 F12 造成的限制 ([#27](https://github.com/TevinLi/amWiki/issues/27))
---
### v0.7.7
_2016-12-19_
- 全文库搜索,增加大小写忽略 ([#24](https://github.com/TevinLi/amWiki/issues/24))
- 修复导出后,在 github wiki 页内目录无法跳转的问题 ([#20](https://github.com/TevinLi/amWiki/issues/20#issuecomment-266927563))
- 修复导出时html 格式的 img src 相对地址未转换问题 ([#23](https://github.com/TevinLi/amWiki/issues/23))
- 修复导出时,链接与 a 标签相对地址未转换问题 ([#23](https://github.com/TevinLi/amWiki/issues/23))
### v0.7.6
_2016-12-11_
- 增加文库导出模块,可以将文库导出为 Github Wiki 项目 (**[#20](https://github.com/TevinLi/amWiki/issues/20)**)
- 允许 `.md` 文件文件名带空格 ([#19](https://github.com/TevinLi/amWiki/issues/19))
- Web 移动端点击页内目录以外的区域将折叠目录 ([#22](https://github.com/TevinLi/amWiki/issues/22))
- 修复某些情况下跳页面不返回顶部的问题 ([#21](https://github.com/TevinLi/amWiki/issues/21))
### v0.7.5
_2016-12-03_
(none)
### v0.7.4
_2016-12-03_
- 增加 Web 端右侧目录悬浮窗 ([#15](https://github.com/TevinLi/amWiki/issues/15))
- 增加对 `[TOC]` 目录生成标签的支持 ([#15](https://github.com/TevinLi/amWiki/issues/15))
- 修复工作端抽取页内目录含单双引号、圆括号时无法正确解析的问题 ([#16](https://github.com/TevinLi/amWiki/pull/16))
- 修复工作端抽取页内目录无法跳转的问题 ([#15](https://github.com/TevinLi/amWiki/issues/15))
- 修复工作端抽取页内目录时内容为空报错的问题 ([#13](https://github.com/TevinLi/amWiki/issues/13), [#14](https://github.com/TevinLi/amWiki/pull/14))
- 修复 Web 端浏览器后退按钮报错问题
- Atom 菜单栏添加 “关于amWiki” 按钮
### v0.7.3
_2016-10-28_

View File

@ -1,4 +1,4 @@
# amWiki 贡献指引
- 如果您是 coder您的所有 issues 或 pull requests请在[Github项目托管地址](https://github.com/TevinLi/amWiki)提交
- 如果您是非技术人员,需要提问或建议请加QQ群347125653
- 如果您需要直接提问或建议请加QQ群347125653

148
README.md
View File

@ -1,133 +1,69 @@
# amWiki 轻文库
![amWiki logo](https://amwiki.xf09.net/docs/assets/logo.png)
amWiki 是一款基于 Javascript 脚本语言、依赖 Atom 编辑器、使用 Markdown 标记语法的轻量级开源 wiki 文库系统。
amWiki 致力于让大家可以更简单、更便捷的建设个人和团队文库系统
amWiki 是一款由 JS 开发、依赖 Atom 或 Nodejs-Npm 的 Markdown 轻量级前端化开源文库系统。
amWiki 致力于让大家可以更简单、更便捷的建设个人和团队文库!
**传送门:**
[amWiki 项目 Github 托管地址](https://github.com/TevinLi/amWiki "amWiki项目Github托管地址")
[![amWiki github star](https://amwiki.xf09.net/githubicons/Tevinli/amWiki/star) ![amWiki github fork](https://amwiki.xf09.net/githubicons/Tevinli/amWiki/fork)](https://github.com/TevinLi/amWiki)
[[fork amWiki on Github](https://github.com/TevinLi/amWiki)]
### amWiki优势
GitHub:
[![](https://img.shields.io/github/stars/TevinLi/amWiki.svg?style=social&label=Star)](https://github.com/TevinLi/amWiki "GitHub Stars") [![](https://img.shields.io/github/forks/TevinLi/amWiki.svg?style=social&label=Fork)](https://github.com/TevinLi/amWiki "GitHub Forks") [![](https://img.shields.io/github/issues-raw/TevinLi/amWiki.svg)](https://github.com/TevinLi/amWiki "GitHub Open Issues") [![](https://img.shields.io/github/issues-closed-raw/TevinLi/amWiki.svg)](https://github.com/TevinLi/amWiki "GitHub Closed Issues")
[![GitHub Tag](https://img.shields.io/github/tag/TevinLi/amWiki.svg)](https://github.com/TevinLi/amWiki "GitHub Tag") [![apm](https://img.shields.io/github/license/TevinLi/amWiki.svg)](https://atom.io/packages/amWiki "MIT License")
APM:
[![APM Version](https://img.shields.io/apm/v/amWiki.svg)](https://atom.io/packages/amWiki "APM Version") [![APM Downloads](https://img.shields.io/apm/dm/amWiki.svg)](https://atom.io/packages/amWiki "APM Downloads")
NPM:
[![NPM Version](https://img.shields.io/npm/v/amwiki.svg)](https://www.npmjs.com/package/amwiki "NPM Version") [![APM Downloads](https://img.shields.io/npm/dt/amwiki.svg)](https://www.npmjs.com/package/amwiki "APM Downloads") [![npm](https://img.shields.io/npm/dm/amwiki.svg)](https://www.npmjs.com/package/amwiki)
<br>
## amWiki 的优势
- 文档系统采用 markdown 语法
- 不用数据库,文档使用 `.md` 格式保存本地文件
- 无需服务端开发,只需支持 http 静态访问网页空间
- 一键创建新的文库
- 自动更新文库导航目录
- 支持多级目录
- 支持截图直接粘帖为本地 png 并插入当前 markdown
- 文档web端自适应显示适合所有平台
- 支持接口文档自动抓取内容生成简单的ajax测试
- 无需服务端的全文库标题内容搜索与计分排序
- ... (更多内容期待您的发现)
### web端效果演示
一键创建新文库将自动生成一套Web端html页面默认生成效果一览[https://tevinli.github.io/amWiki/](https://tevinli.github.io/amWiki/index.html)
<br>
## 如何开始
## 安装 amWiki
amWiki 可以同时在 Atom 编辑器和 nodejs npm 的命令行两个平台工作,两个平台的工作相互独立,但所创建的文库却可以相互共用
PS对这两个平台的依赖都是编辑需求而不是服务器需求amWiki 创建的文库是纯静态 html可以布置到任意服务器
1. 下载Github开源文本编辑器 [Atom 官网](https://atom.io/ "Atom官网"),并安装
### 作为 Atom 插件安装
1. 下载 Github 开源文本编辑器 [Atom](https://atom.io/ "Atom官网"),并安装
2. 安装 Atom 插件 amWiki并在完成后重启 Atom
- 前往 Github 的 [amWiki版本发布页](https://github.com/TevinLi/amWiki/releases) 下载最新版压缩包,解压到 C:\Users\Administrator\.atom\packages并将文件夹名 `amWiki-1.x.x` 改为 `amWiki`
- 或者Atom 菜单File -> Setting -> Install -> 搜索 `amWiki` -> 找到 amWiki 并 Install
- 或者,在 cmd 或终端中命令:`apm install amWiki`_第三字母 W 大写_
3. 在 Atom 菜单File -> `Add Project Folder` 添加一个项目文件夹
4. 在此文件夹下创建一个名为 `config.json` 的文件
5. 在 Atom 菜单amWiki轻文库 -> 通过“config.json”创建新文库
2. 安装Atom插件amWiki并重启Atom
- Atom菜单File -> Setting -> Install -> 搜索`amWiki`
- 或者运行cmd`apm install amWiki`
- 或者从Github的 [amWiki版本发布](https://github.com/TevinLi/amWiki/releases) 下载zip解压到`C:\Users\Administrator\.atom\packages`,并将文件夹名`amWiki-master`改为`amWiki`
### 作为 nodejs 全局模块安装
1. 下载 [nodejs](https://nodejs.org/) 并安装
2. 执行命令: `npm install -g amwiki`_第三字母 w 小写_
3. cd 到某个文件夹,通过命令 `amwiki create` 创建文库
4. 通过命令 `amwiki help` 查看帮助
3. (在本地服务器静态目录)创建一个文件夹
<br>
4. 在Atom中 **添加项目文件夹**,并指向刚创建的文件夹
## amWiki 教程 & 文档
更多详细功能与使用介绍请前往: [ [amWiki 官网文档中心amwiki.org/doc/](http://amwiki.org/doc/) ]
(amWiki 的文档由项目自身创建与维护)
5. 在Atom刚创建的项目下新建`config.json`文件,并输入一下内容:
```javascript
{
"name": "", //您的文库名称,设置但为空或不设置将显示默认名
"ver": "", //文本版本号或维护者名号设置但为空将不显示注意诺不设置此属性将显示amWiki作者
"logo": "", //logo的url设置但为空或不设置将显示默认logo
"testing": true, //是否启用接口测试默认值false
"colour": "" //自定义颜色,默认为蓝色
}
```
6. 点击Atom菜单
Packages -> amWiki文库 -> 通过“config.json”创建新文库
7. 此时项目中自动创建了许多内容,其中 library 文件夹即为您的文档库,编辑您的文库文档
8. 使用 F12 启动本地静态服务器,访问刚刚自动创建的 index.html
## 目录结构
自动生创建的内容目录如下
index.html //http访问首页
amWiki/ //amWiki工作文件目录
library/ //您的markdown文库目录所有文件必须使用.md格式
├ $navigation.md //amWiki文库目录导航文件可自动/手动更新
├ 首页.md //内容区默认显示内容
├ 01-关于amWiki文库/ //markdown文件夹01
│ ├ 001-关于amWiki //一些markdown文档支持二级目录
│ └ 002-...
├ 02-学习markdown/ //markdown文件夹02
│ ├ 001-md入门 //一些markdown文档支持二级目录
│ └ 002-...
└ 03-... //更多markdown文件夹
(assetes/) //如果您粘帖截图,图片文件将自动创建在此处
## 如何编辑
1. 新建或更改文件夹和文档名,组织您自己的文档结构 (文档必须markdown文档、**使用.md扩展名**)
2. 编辑markdown文档制作您自己的文档内容
3. 每个文件夹或文件使用“id-名称”来实现命名排序请勿删除id删除后将无法正常工作
id仅允许整数或浮点数类型且使用连接符或下划线与具体名称组成命名
4. library文件夹下`首页.md`为打开时默认的或url参数错误时的显示内容
5. `$navigation.md`导航文件无需人工维护,创建新文件夹或文件时将自动更新,也可以在菜单栏手动刷新:
菜单栏 -> amWiki文库 -> 手动更新当前文库导航文件
6. 如果需要在markdown文档中插入图片**请先截图**,然后在.md文档对应位置使用快捷键`Ctrl + Shift + V`
7. 对于较长文章,可以使用页内目录,依次进行如下操作,或使用快捷键`Ctrl + D`,即可在光标处插入页内目录。
菜单栏 -> amWiki文库 -> 提取h2、h3标题为页内目录
注意请按顺序使用h1、h2、h3且h1仅使用一次。
8. 如果启用了测试,对于文档中**同时存在“请求地址”“请求类型”“请求参数”三个h3标题**的文档,自动在右上角激活接口测试功能
- 请求参数的表格,请按`参数名、类型、必填、描述、默认值`的顺序建立表格,否则不能正常抓取
- 只能请求同域接口,不能跨域
- 如果接口需要登录权限,请先登录您自己的系统
9. 本插件升级后,您想更新`项目/amWiki/`文件夹下web端的工作文件您只需重新打开`config.json`文件然后在Atom菜单上选择`通过“config.json”创建新文库`即可。
这个二次创建操作不会影响您 library 与 assetes 文件夹下的内容。
10. 借助版本管理 SVN、Git、Hg传输协议FTP/SFTP文件同步Dropbox、百度云等等工具便捷实现网络访问。
## 如何使用
1. 使用 http 访问项目的 index.html。
2. PC端使用左侧导航栏、移动端使用右上角导航弹出菜单切换页面。
3. 在导航栏顶部,可以使用筛选功能整个导航目录进行筛选。
4. 如果存在页内目录直接点击页内目录使用hash滚动同时您可以直接带hash分享以方便他人快速浏览指定内容。
5. 如果页面激活了接口测试功能点击右上角“测试接口”打开测试面板输出参数并点击“发送Ajax”即可看到响应内容。
## 后记
这个项目的初始灵感来自 MDwiki这是一个使用 html5/js 纯静态的 markdown wiki 系统。
一开始,我也准备使用它来写一些文档,但是我很快发现,如果我新增一篇文档,需要我手动去编辑导航栏文件增加一条导航,这个体验并不友好。然后想写一套类似 [微信公众平台开发者文档](http://mp.weixin.qq.com/wiki/home/index.html) 这样较大的文档库,发现 MDwiki 由于其容量所限,并不能很好的胜任。
于是经过一番折腾就有了amWiki。
个人开源不易欢迎来github上star支持一下如果发现问题或好的建议请尽量在github上发issues不胜感谢
<br>
## 更新日志
### 如何获取最新版本信息与动态?
- 欢迎添加作者微信为好友,微信号 **XF90_Net**
_(大家加微信的时候都尽量备注一下原因吧,没有备注的不加哦!目前主要集中精力在开发,暂不接受各种调戏跟闲聊~)_
- 其他途径: QQ群`347125653` / [微博](http://weibo.com/1706602953/profile?is_all=1) / [Twitter](https://twitter.com/tevinli2009)
- QQ群 **347125653**
- 如果可以,欢迎直接加我微信
![](http://img.amwiki.org/global/wx.qrcode.150.png)
### 如何查看历史版本日志?
[amWiki 版本更新日志](https://github.com/TevinLi/amWiki/blob/master/CHANGELOG.md "amWiki版本更新日志")

15
SCHEDULE.md Normal file
View File

@ -0,0 +1,15 @@
# amWiki 开发计划表
## v1.2.1
- 增加修改 web 端 library 目录路径的配置项 #47
- markdown 基础语法扩展,允许使用高亮标签对文字飘红
- 目录生成优化,识别标题是否已经带有序号 #33
## v2.0.0 _(under way)_
- 重新设计 Web 端架构,大幅强化可扩展性
 - 设计可扩展的管理、通讯、调用机制
 - 设计 Web 端插件模块机制
 - 设计 Web 端自定义主题模块机制
 - 分离原有功能为独立模块

186
bin/main.js Normal file
View File

@ -0,0 +1,186 @@
#!/usr/bin/env node
/**
* 工作端 - 终端(命令行) - 入口模块
*/
const fs = require('fs');
//获取命令内容
let [nodePath, mainPath, command, ...parameters] = process.argv;
//转换 linux 下软链接路径为真实路径
mainPath = fs.realpathSync(mainPath);
// co 模块,异步函数编程
const co = require('../modules/co');
//在全局变量上绑定 alert、confirm2、prompt2 方法,作为命令行输出与交互
({alert: global.alert, confirm2: global.confirm2, prompt2: global.prompt2} = require('./messageBox'));
// wiki 创建器
const creator = require('../build/creator');
//文库管理
const mngWiki = require('../build/manageWiki');
//文件夹管理
const mngFolder = require('../build/manageFolder');
//手动刷新导航文件
const makeNav = require('../build/makeNavigation');
//手动刷新页面挂载数据
const makeMut = require('../build/makeMounts');
//本地服务器模块
const localServer = require('../build/localServer');
//项目导出模块
const exportGithub = require('../build/exportGithub');
//格式化显示
const printFn = require('./printf');
//读取项目包配置
const packageConf = JSON.parse(fs.readFileSync(mainPath.split(/bin[\\\/]main/)[0] + 'package.json', 'utf-8'));
//项目根目录
const root = mngFolder.isAmWiki(process.cwd());
const wikis = {};
//注册文库
mngWiki.linkWikis(wikis);
if (root) {
mngWiki.addWiki(root);
}
co(function*() {
//执行命令
switch (command) {
//依据引导创建 wiki
case 'init':
break;
//依据 config.json 创建 wiki
case 'create':
case '-c':
// config.json 文件路径
const configPath = process.cwd().replace(/\\/g, '/') + '/config.json';
//项目 files 文件夹路径
const filesPath = mainPath.replace(/\\/g, '/').split('bin')[0] + 'files/';
//开始创建
const root2 = yield creator.create(configPath, filesPath, packageConf);
if (!root2) {
break;
}
//更新导航
makeNav.refresh(root2 + 'library/');
makeMut.make(root, true);
break;
//更新 wiki
case 'update':
case '-u':
if (!root) {
console.error('非 amWiki 项目文件夹,无法更新导航!');
break;
}
const type = parameters[0];
//更新导航
if (type === 'nav') {
makeNav.refresh(root);
}
//更新文库嵌入数据
else if (type === 'mut') {
makeMut.make(root);
}
//更新 SEO 模块
else if (type === 'seo') {
}
//完整更新
else if (typeof type === 'undefined') {
makeNav.refresh(root);
makeMut.make(root, true);
}
break;
//启动本地服务器
case 'server':
case '-s':
if (!root) {
console.error('非 amWiki 项目文件夹,无法启动服务器!');
break;
}
const port = parameters[0];
const noIndex = parameters[1] || '';
//有输入端口
if (typeof port !== 'undefined') {
//端口合法
if (/^\d+$/.test(port)) {
yield localServer.run(wikis, port);
if (noIndex === 'no-index') {
localServer.setOffIndex();
}
}
//端口写成 noIndex
else {
yield localServer.run(wikis);
if (port === 'no-index' || noIndex === 'no-index') {
localServer.setOffIndex();
}
}
}
//没输入端口号
else {
yield localServer.run(wikis);
}
break;
//本地浏览文档
case 'browser':
case '-b':
if (!root) {
console.error('非 amWiki 项目文件夹,无法浏览文库!');
break;
}
const fileId = parameters[0];
//未给出需要浏览的文档id
if (typeof fileId === 'undefined') {
yield localServer.browser(root + 'library/', wikis);
}
//有给出需要浏览的文档id
else {
}
break;
//导出 wiki
case 'export':
case '-e':
if (!root) {
console.error('非amWiki项目文件夹无法导出');
break;
}
if (parameters[0] !== 'github-wiki') {
console.error('除 github-wiki 外,目前不支持其他类型导出!');
break;
}
let outPath = parameters[1];
if (typeof outPath === 'undefined') {
outPath = yield prompt2('请输入导出文件夹:');
}
yield exportGithub.export(root, outPath.replace(/\\/g, '/'));
break;
//显示版本号
case 'version':
case '-v':
printFn.ver(packageConf);
break;
//显示帮助
case 'help':
case '-h':
default:
printFn.help();
break;
}
//关闭用户输入
process.stdin.on('error', (e) => {
if (e.code !== 'EPIPE' || e.syscall !== 'shutdown') {
throw e;
}
});
process.stdin.end();
}).catch((e) => {
console.error(e);
});

85
bin/messageBox.js Normal file
View File

@ -0,0 +1,85 @@
/**
* 工作端 - 终端(命令行) - 模拟浏览器 alertconfirmprompt 方法进行信息交互
* @author Tevin
*
* @example
* co(function* () {
* const c = yield confirm2('测试confirm2');
* console.log(c);
* const p = yield prompt2('测试prompt2');
* console.log(p);
* process.stdin.end();
* });
*
*/
/**
* 模拟消息框
* @param {String} msg
*/
const alert = (msg) => {
msg = typeof msg !== 'string' ? String(msg) : msg;
process.stdout.write(msg + '\n')
};
/**
* 模拟确认框回调传参为用户选择的布尔值
* @param {String} msg
* @returns {Promise}
*/
const confirm2 = (msg) => {
return new Promise((resolve, reject) => {
msg = typeof msg !== 'string' ? String(msg) : msg;
let readStdin = () => {
let chunk = process.stdin.read();
if (chunk !== null) {
chunk = chunk.replace(/[\n\r\u0004]*$/, '');
if (/^(y|yes|ok)$/i.test(chunk)) {
resolve(true);
} else {
resolve(false);
}
readStdin = null;
}
};
process.stdout.write(msg + '(y/n) ');
process.stdin.setEncoding('utf8');
process.stdin.on('readable', () => {
readStdin && readStdin();
});
});
};
/**
* 模拟输入框回调传参为用户输入的字符串
* @param {String} msg
* @returns {Promise}
*/
const prompt2 = (msg) => {
return new Promise((resolve, reject) => {
msg = typeof msg !== 'string' ? String(msg) : msg;
let readStdin = () => {
let chunk = process.stdin.read();
if (chunk !== null) {
chunk = chunk.replace(/[\n\r\u0004]*$/, '');
resolve(chunk);
readStdin = null;
}
};
process.stdout.write(msg + ' ');
process.stdin.setEncoding('utf8');
process.stdin.on('readable', () => {
readStdin && readStdin();
});
});
};
const messageBox = (function () {
return {
alert: alert,
confirm2: confirm2,
prompt2: prompt2
};
})();
module.exports = messageBox;

140
bin/printf.js Normal file
View File

@ -0,0 +1,140 @@
/**
* 工作端 - 终端(命令行) - 着色打印
* @author Tevin
*/
const fs = require('fs');
//命令行彩色输出简化command line color
const clc = (colorId, text) => `\x1b[${colorId}m${text}\x1b[0m`;
//文件与文件夹创建操作着色打印
const {mkdir, mkdirSync, writeFile, writeFileSync} = fs;
fs.mkdir = (path, mode, callback) => {
alert(` ${clc(36, 'create')} ${path}`);
return mkdir(path, mode, callback);
};
fs.mkdirSync = (path, mode) => {
alert(` ${clc(36, 'create')} ${path}`);
return mkdirSync(path, mode);
};
fs.writeFile = (path, data, options, callback) => {
alert(` ${clc(36, 'create')} ${path}`);
return writeFile(path, data, options, callback);
};
fs.writeFileSync = (path, data, options) => {
alert(` ${clc(36, 'create')} ${path}`);
return writeFileSync(path, data, options);
};
//文件与文件夹删除操作着色打印
const {rmdir, rmdirSync, unlink, unlinkSync} = fs;
fs.rmdir = (path, callback) => {
alert(` ${clc(31, 'delete')} ${path}`);
return rmdir(path, callback);
};
fs.rmdirSync = (path) => {
alert(` ${clc(31, 'delete')} ${path}`);
return rmdirSync(path);
};
fs.unlink = (path, callback) => {
alert(` ${clc(31, 'delete')} ${path}`);
return unlink(path, callback);
};
fs.unlinkSync = (path) => {
alert(` ${clc(31, 'delete')} ${path}`);
return unlinkSync(path);
};
//终端输出操作着色打印
const {info, warn, error} = console;
console.info = (...content) => {
return console.log(`${clc(46, 'INFO:')}`, ...content);
};
console.warn = (...content) => {
return console.log(`${clc(43, 'WARN:')}`, ...content);
};
console.error = (...content) => {
return console.log(`${clc(41, 'ERROR:')}`, ...content);
};
//帮助文本
const helpWords = `
用法
amwiki [command] [arguments]
命令列表
-h, help 显示本帮助
-v, version 显示版本号
-c, create 创建 wiki
-u, update 更新 wiki 数据默认根据文库配置选择适当内容更新
可选参数
[nav|mut]
指定仅更新导航
指定仅更新页面挂载数据
${clc(90, '(eg: amwik update nav)')}
-s, server 启动本地服务器默认本地服务器使用端口号 5171
可选参数
[8080] 重新指定端口号
[no-index] 关闭 amwiki 索引页
${clc(90, '(eg: amwiki server 8080 no-index)')}
-b, browser 本地浏览文档默认访问文库首页
可选参数
[002/001] 按目录文档的id打开指定文档(待开发)
${clc(90, '(eg: amwiki browser 002/001)')}
-e, export amwiki 项目导出
必需参数
github-wiki 指定导出类型为 github 项目附属 wiki
d:\/your-path 指定本次导出的本地输出路径
${clc(90, '(eg: amwiki export github-wiki d:\/your-path)')}
更多文档
http://amwiki.org/doc/
`;
//[seo] 仅更新SEO模块(待开发)
//amWiki logo
const vesWords = `
kk kk kk kk ww
wwwwww www www kk kk kk
ww ww ww ww kk kk kk kk kk kk kk
wwwwwww ww ww ww kk kkkk kk kk kk kk kk
ww ww ww ww ww kk kk kk kk kk kkkk kk
wwwwwww ww ww ww kk kk kk kk kk kk
`.replace(/w/g, `${clc(32, '$')}`).replace(/k/g, `${clc(36, '$')}`);
const printf = (function () {
return {
/**
* 命令行彩色输出
* @public
*/
clc: clc,
/**
* 显示帮助信息
* @public
*/
help: function () {
alert(helpWords);
},
/**
* 显示版本信息
* @param {Object} packageConf
* @public
*/
ver: function (packageConf) {
const text = [
'Package: amWiki',
`Version: ${clc(0, packageConf.version)}`,
'Author: Tevin Li',
'HomePage: amwiki.org',
'Fork: github.com/tevinli/amWiki',
'License: MIT'
];
alert(vesWords + text.join('\n') + '\n');
}
};
})();
module.exports = printf;

333
build/creator.js Normal file
View File

@ -0,0 +1,333 @@
/**
* 工作端 - 输出 web 静态资源模块
* @author Tevin
*/
const fs = require('fs');
const co = require('../modules/co');
const mngWiki = require('./manageWiki');
const mngFolder = require('./manageFolder');
const makeNav = require('./makeNavigation');
const creator = (function () {
return {
/**
* 复制文件
* @param {String} from
* @param {String} to
* @private
*/
_copyFile: function (from, to) {
fs.createReadStream(from).pipe(fs.createWriteStream(to));
},
/**
* 创建 amWiki 必要的文件夹
* @param {String} outputPath
* @returns {Boolean}
* @private
*/
_createWikiFolder: function (outputPath) {
if (!fs.existsSync(outputPath + 'amWiki/')) {
fs.mkdirSync(outputPath + 'amWiki/', 0o777);
}
if (!fs.existsSync(outputPath + 'amWiki/js/')) {
fs.mkdirSync(outputPath + 'amWiki/js/', 0o777);
}
if (!fs.existsSync(outputPath + 'amWiki/css/')) {
fs.mkdirSync(outputPath + 'amWiki/css/', 0o777);
}
if (!fs.existsSync(outputPath + 'amWiki/images/')) {
fs.mkdirSync(outputPath + 'amWiki/images/', 0o777);
}
if (!fs.existsSync(outputPath + 'library/')) {
fs.mkdirSync(outputPath + 'library/', 0o777);
if (!fs.existsSync(outputPath + 'library/001-学习amWiki/')) {
fs.mkdirSync(outputPath + 'library/001-学习amWiki/', 0o777);
}
if (!fs.existsSync(outputPath + 'library/001-学习amWiki/05-学习markdown/')) {
fs.mkdirSync(outputPath + 'library/001-学习amWiki/05-学习markdown/', 0o777);
}
if (!fs.existsSync(outputPath + 'library/002-文档示范/')) {
fs.mkdirSync(outputPath + 'library/002-文档示范/', 0o777);
}
return false;
}
return true;
},
/**
* 创建着色色系
* (ps: 颜色变亮变暗不是 rgb 分别同时增加或减少一个值)
* @param {String} color
* @returns {{base: String, light: String, dark: String}} - 生成的浅深三色
* @private
*/
_clacWikiColour: function (color) {
const colours = {};
//修复16进制下的1位数
const lessThan = (str) => {
if (str <= 9) {
str = '0' + str;
} else {
const num = parseInt('0x' + str);
if (num >= 10 && num <= 15) {
str = '0' + str;
}
}
return str;
};
//颜色合法检查
const atBinary16 = /^#([0-9a-fA-F]{3}){1,2}$/.test(color);
const atRGB = /^[rR][gG][bB]([aA])?\(\d{1,3},\d{1,3},\d{1,3}(,.*?)?\)$/.test(color);
const atList = /(blue|default)/.test(color);
if (!atBinary16 && !atRGB && !atList) {
alert('请使用标准16进制颜色、标准RGB颜色、2种特定颜色名指定颜色本次创建使用默认颜色');
return this._clacWikiColour('default');
}
//颜色名称转色值
const colorList = {
default: '#4296eb',
blue: '#4296eb'
};
if (atList) {
color = colorList[color];
}
//解析颜色为rgb
const rgb = [];
if (color.indexOf('#') === 0) {
rgb[0] = parseInt('0x' + color.substr(1, 2));
rgb[1] = parseInt('0x' + color.substr(3, 2));
rgb[2] = parseInt('0x' + color.substr(5, 2));
} else {
const rgbStr = color.split('(')[1].split(')')[0].split(',');
rgb[0] = parseInt(rgbStr[0]);
rgb[1] = parseInt(rgbStr[1]);
rgb[2] = parseInt(rgbStr[2]);
}
//生成主色base
colours.base = '#' + [
lessThan(rgb[0].toString(16)),
lessThan(rgb[1].toString(16)),
lessThan(rgb[2].toString(16))
].join('');
//颜色变化量,越小的值变化越大
const increment = [
Math.round((255 - rgb[0]) / 3),
Math.round((255 - rgb[1]) / 3),
Math.round((255 - rgb[2]) / 3)
];
//生成亮色light
colours.light = '#' + [
(rgb[0] + increment[0]).toString(16),
(rgb[1] + increment[1]).toString(16),
(rgb[2] + increment[2]).toString(16)
].join('');
//生成暗色dark
let larger = 0,
largeIndex = -1;
const largerFd = (i) => {
if (increment[i] > larger) {
larger = increment[i];
largeIndex = i;
}
};
largerFd(0);
largerFd(1);
largerFd(2);
if (rgb[largeIndex] - larger < 0) {
//修复加深颜色值为负的情况最多减到0其他三色等比例减小
const reduce = (rgb[largeIndex] - larger) / larger;
increment[0] += Math.round(increment[0] * reduce);
increment[1] += Math.round(increment[1] * reduce);
increment[2] += Math.round(increment[2] * reduce);
}
colours.dark = '#' + [
lessThan((rgb[0] - increment[0]).toString(16)),
lessThan((rgb[1] - increment[1]).toString(16)),
lessThan((rgb[2] - increment[2]).toString(16))
].join('');
return colours;
},
/**
* 配置检查
* @param {String} configPath
* @param {String} filesPath
* @returns {Promise}
* @private
*/
_checkConfig: function (configPath, filesPath) {
const that = this;
return co(function*() {
const options = {};
//创建路径
options.filesPath = filesPath;
options.outputPath = configPath.split('config.json')[0].replace(/\\/g, '/');
//读取配置
let configStr = '';
let noConfig = false;
if (fs.existsSync(configPath)) {
configStr = fs.readFileSync(configPath, 'utf-8') || '';
} else {
noConfig = true;
}
if (configStr.length === 0) {
if (yield confirm2('没有读取到任何配置,继续创建么?')) {
configStr = '{}';
if (noConfig) {
fs.writeFileSync(configPath, configStr, 'utf-8');
}
} else {
return;
}
}
//解析默认配置
let parseOk = true,
config = null;
try {
config = JSON.parse(configStr);
} catch (e) {
alert('配置解析失败,请检查您的 config.json 是否有严格按 Json 格式书写!\n错误消息' + e.message);
parseOk = false;
}
if (!parseOk) {
return;
}
return {
options: options,
config: mngWiki.parseConfig(config)
};
}).catch((e) => {
console.error(e);
});
},
/**
* 创建amWiki本地文件
* @param {String} configPath - config.json文件的路径
* @param {String} filesPath - 项目包files文件夹路径
* @returns {Promise} 项目根目录
* @public
*/
create: function (configPath, filesPath) {
const that = this;
return co(function*() {
const {options, config} = yield that._checkConfig(configPath, filesPath);
//开始创建
const files = mngFolder.readFolder(options.outputPath);
if (files.length > 1) {
if (!(yield confirm2('此处已有一些文件或文件夹是否仍然在此创建amWiki'))) {
return false;
}
}
//创建index.html
let indexPage = fs.readFileSync(options.filesPath + 'amWiki.tpl', 'utf-8');
indexPage = indexPage.replace(/\{\{name\}\}/g, config.name)
.replace('{{version}}', config.version)
.replace('{{logo}}', config.logo);
//嵌入配置
indexPage = indexPage.replace('{{config}}', 'AWConfig=' + JSON.stringify(config));
//测试模块
if (config.testing) {
const testingTpl = fs.readFileSync(options.filesPath + 'amWiki.testing.tpl', 'utf-8');
const testingScript = '<script src="amWiki/js/amWiki.testing.js"></script>';
indexPage = indexPage
.replace('{{amWiki.testing.tpl}}', testingTpl)
.replace('{{amWiki.testing.js}}', testingScript);
} else {
indexPage = indexPage
.replace('{{amWiki.testing.tpl}}', '')
.replace('{{amWiki.testing.js}}', '');
}
//自定义 css、js 嵌入
if (config.imports) {
let item,
cssList = '',
jsList = '';
for (item of config.imports.css) {
cssList = '<link rel="stylesheet" type="text/css" href="' + item + '" />'
}
for (item of config.imports.js) {
jsList = '<script type="text/javascript" src="' + item + '"></script>'
}
indexPage = indexPage
.replace('{{custom.css}}', cssList)
.replace('{{custom.js}}', jsList);
} else {
indexPage = indexPage
.replace('{{custom.css}}', '')
.replace('{{custom.js}}', '');
}
fs.writeFileSync(options.outputPath + 'index.html', indexPage, 'utf-8');
//创建文件夹
const hasLibrary = that._createWikiFolder(options.outputPath);
//创建amWiki.css
let wikiCss = fs.readFileSync(options.filesPath + 'amWiki.css', 'utf-8');
if (config.testing) {
wikiCss += fs.readFileSync(options.filesPath + 'amWiki.testing.css', 'utf-8');
}
wikiCss += fs.readFileSync(options.filesPath + 'amWiki.print.css', 'utf-8');
wikiCss += fs.readFileSync(options.filesPath + 'amWiki.search.css', 'utf-8');
let colors = that._clacWikiColour(config.colour);
wikiCss = wikiCss
.replace(/@colour-base/g, colors.base)
.replace(/@colour-light/g, colors.light)
.replace(/@colour-dark/g, colors.dark);
fs.writeFileSync(options.outputPath + 'amWiki/css/amWiki.css', wikiCss, 'utf-8');
//拷贝页面资源
const fileList = [
['markdownbody.github.css', 'amWiki/css/markdownbody.github.css'],
['lhjs.github-gist.css', 'amWiki/css/lhjs.github-gist.css'],
['gbk.js', 'amWiki/js/gbk.js'],
['pinyin.js', 'amWiki/js/pinyin.js'],
['flowchart.min.js', 'amWiki/js/flowchart.min.js'],
['raphael-min.js', 'amWiki/js/raphael-min.js'],
['jquery-compat-3.1.0.min.js', 'amWiki/js/jquery-compat-3.1.0.min.js'],
['marked.min.js', 'amWiki/js/marked.min.js'],
['highlight.pack.js', 'amWiki/js/highlight.pack.js'],
['amWiki.js', 'amWiki/js/amWiki.js'],
['amWiki.scrollbar.js', 'amWiki/js/amWiki.scrollbar.js'],
['amWiki.imgsView.js', 'amWiki/js/amWiki.imgsView.js'],
['amWiki.testing.js', 'amWiki/js/amWiki.testing.js'],
['amWiki.tools.js', 'amWiki/js/amWiki.tools.js'],
['amWiki.storage.js', 'amWiki/js/amWiki.storage.js'],
['amWiki.docs.js', 'amWiki/js/amWiki.docs.js'],
['amWiki.search.js', 'amWiki/js/amWiki.search.js'],
['amWiki.search.worker.js', 'amWiki/js/amWiki.search.worker.js'],
['icons.svg', 'amWiki/images/icons.svg'],
['logo.png', 'amWiki/images/logo.png'],
['menubar_bg.png', 'amWiki/images/menubar_bg.png']
];
for (let file of fileList) {
that._copyFile(options.filesPath + file[0], options.outputPath + file[1]);
}
//如果没有library则复制一套默认文档
if (!hasLibrary) {
//首页文档
const home = fs.readFileSync(options.filesPath + 'doc.home.md', 'utf-8').replace('{{name}}', config.name);
fs.writeFileSync(options.outputPath + 'library/home-首页.md', home, 'utf-8');
//其他页面文档
let fileList2 = [
['doc.amwiki-introduce.md', 'library/001-学习amWiki/01-amWiki轻文库简介.md'],
['doc.amwiki-mind-map.md', 'library/001-学习amWiki/02-amWiki功能导图.md'],
['doc.amwiki-new.md', 'library/001-学习amWiki/03-如何开始一个新amWiki轻文库.md'],
['doc.amwiki-edit.md', 'library/001-学习amWiki/04-如何编辑amWiki轻文库.md'],
['doc.amwiki-testing.md', 'library/001-学习amWiki/06-使用测试模块测试接口.md'],
['doc.amwiki-test-cross.md', 'library/001-学习amWiki/07-amWiki转接到任意域名进行接口测试.md'],
['doc.md-start.md', 'library/001-学习amWiki/05-学习markdown/01-Markdown快速开始.md'],
['doc.md-high-lighting.md', 'library/001-学习amWiki/05-学习markdown/02-amWiki与语法高亮.md'],
['doc.md-flow-chart.md', 'library/001-学习amWiki/05-学习markdown/03-amWiki与流程图.md'],
['doc.md-atom.md', 'library/001-学习amWiki/05-学习markdown/05-Atom对Markdown的原生支持.md'],
['doc.demo-api.md', 'library/002-文档示范/001-通用API接口文档示例.md'],
['doc.demo-long-article.md', 'library/002-文档示范/002-超长文档页内目录示例.md']
];
for (let file of fileList2) {
that._copyFile(options.filesPath + file[0], options.outputPath + file[1]);
}
}
return options.outputPath;
}).catch((e) => {
console.error(e);
});
}
};
})();
module.exports = creator;

314
build/exportGithub.js Normal file
View File

@ -0,0 +1,314 @@
/**
* 工作端 - 导出 github-wiki 模块
* @author Tevin
*/
const fs = require('fs');
const co = require('../modules/co');
const mngFolder = require('./manageFolder');
const mngWiki = require('./manageWiki');
const exportGithub = (function () {
return {
/**
* 拷贝一张图片
* @param {String} from
* @param {String} to
* @private
*/
_copyImg: function (from, to) {
fs.createReadStream(from).pipe(fs.createWriteStream(to));
},
/**
* 拷贝一篇文档
* @param {String} fileName
* @param {String} pathTo
* @private
*/
_copyMd: function (fileName, pathTo) {
let file = fs.readFileSync(fileName[0], 'utf-8');
//相对路径图片地址转换
file = file
.replace(/!\[(.*?)]\(assets(.*?)\)/g, '![$1](' + this._githubUrl + 'wiki/images$2)')
.replace(/\[(.*?)]\(assets(.*?)\)/g, '[$1](' + this._githubUrl + 'wiki/images$2)')
.replace(/<img(.*?)src="assets(.*?)"/g, '<img$1src="' + this._githubUrl + 'wiki/images$2"')
.replace(/<a(.*?)href="assets(.*?)"/g, '<a$1href="' + this._githubUrl + 'wiki/images$2"');
//页内目录 github 规则转换
file = file.replace(/\[(.*?)]\(#(.*?) "(.*?)"\)/g, function (match, s1, s2, s3) {
s2 = s2
.replace(/( |%20)/g, '-')
.replace(/[^a-zA-Z0-9\u4e00-\u9fa5-_]/g, '')
.toLocaleLowerCase();
return '[' + s1 + '](#' + s2 + ' "' + s3 + '")';
});
fs.writeFileSync(pathTo + '/' + fileName[1], file, 'utf-8');
},
/**
* 导出导航底部签名
* @param {[Array]} fileList
* @param {[Array]} duplicate
* @param {String} pathFrom
* @param {String} pathTo
* @private
*/
_exportNavigation: function (fileList, duplicate, pathFrom, pathTo) {
let navigation = fs.readFileSync(pathFrom + '/$navigation.md', 'utf-8');
const list = fileList.concat(duplicate);
let path, name;
for (let item of list) {
path = item[0]
.replace(/\\/g, '/')
.split('library/')[1]
.replace(/\.md$/, '');
name = item[1]
.replace(/\.md$/, '')
.replace(/ /g, '%20');
navigation = navigation.replace('?file=' + path, this._githubUrl + 'wiki/' + name);
}
navigation = navigation
.replace(/\?file=(home[-_].*?\))|(首页\))/, this._githubUrl + 'wiki)')
.replace(/#### \[(.*?)]/, '## WIKI 导航\n\n##### [$1]');
navigation += '\n\n﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊ \n' +
'**This wiki is created by [[amWiki](http://amwiki.org)]**';
fs.writeFileSync(pathTo + '/_Sidebar.md', navigation, 'utf-8');
},
/**
* 导出首页
* @param {String} pathFrom
* @param {String} pathTo
* @private
*/
_exportHome: function (pathFrom, pathTo) {
let file = null;
for (let fileName of mngFolder.readFolder(pathFrom)) {
if (/^home[-_].*?\.md$/.test(fileName) || fileName === '首页.md') {
file = fs.readFileSync(pathFrom + '/' + fileName, 'utf-8');
}
}
file = file
//相对路径图片地址转换
.replace(/!\[(.*?)]\(assets(.*?)\)/g, '![$1](' + this._githubUrl + 'wiki/images$2)')
.replace(/\[(.*?)]\(assets(.*?)\)/g, '[$1](' + this._githubUrl + 'wiki/images$2)')
.replace(/<img(.*?)src="assets(.*?)"/g, '<img$1src="' + this._githubUrl + 'wiki/images$2"')
.replace(/<a(.*?)href="assets(.*?)"/g, '<a$1href="' + this._githubUrl + 'wiki/images$2"')
//logo复制与图片引用地址转换
.replace('amWiki/images/logo.png', () => {
this._copyImg(pathFrom + '../amWiki/images/logo.png', pathTo + '/images/amWiki-logo.png');
return this._githubUrl + 'wiki/images/amWiki-logo.png';
});
fs.writeFileSync(pathTo + '/Home.md', file, 'utf-8');
},
/**
* 导出图片
* @param {String} pathFrom
* @param {String} pathTo
* @private
*/
_exportImage: function (pathFrom, pathTo) {
if (!fs.existsSync(pathTo + '/images/')) {
fs.mkdirSync(pathTo + '/images/', 0o777);
}
if (fs.existsSync(pathFrom + '../assets/')) {
mngFolder.copyFolder(pathFrom + '../assets/', pathTo + '/images/');
}
},
/**
* 检查重名
* @param {[Array]} fileList
* @returns {[Array]}
* @private
*/
_checkDuplicate: function (fileList) {
const dup = [];
for (let i = 0, item; item = fileList[i]; i++) {
for (let j = i + 1, item2; item2 = fileList[j]; j++) {
if (item[1] === item2[1]) {
dup.push([item, item2]);
}
}
}
return dup;
},
/**
* 导出重复文档
* @param {Array} list
* @param {String} pathTo
* @private
*/
_exportDuplicate: function (list, pathTo) {
if (list.length === 0) {
return;
}
const checkExist = (fileName) => {
//如果已存在,增加空格
if (fs.existsSync(pathTo + '/' + fileName[1])) {
fileName[1] = fileName[1].replace(/\.md$/, ' .md');
checkExist(fileName);
}
//不存在正常复制
else {
this._copyMd(fileName, pathTo);
}
};
for (let fileName of list) {
checkExist(fileName);
}
},
/**
* 导出普通文档
* @param {Array} list
* @param {String} pathTo
* @private
*/
_exportNormal: function (list, pathTo) {
if (list.length === 0) {
return;
}
for (let fileName of list) {
this._copyMd(fileName, pathTo);
}
},
/**
* 开始导出
* @param {String} pathFrom
* @param {String} pathTo
* @param {[Array]} fileList
* @param {[Array]} duplicates
* @returns {Promise}
* @private
*/
_toExport: function (pathFrom, pathTo, fileList, duplicates) {
const that = this;
return co(function* () {
if (mngFolder.readFolder(pathTo).length > 0 && (yield confirm2('所选导出文件夹不为空,是否需要清空?'))) {
mngFolder.cleanFolder(pathTo);
}
let fileList2 = [];
const duplicate2 = [];
if (typeof duplicates !== 'undefined') {
for (let file of fileList) {
let fileDup = false;
for (let dup of duplicates) {
if (file[0] === dup) {
fileDup = true;
duplicate2.push(file);
break;
}
}
if (!fileDup) {
fileList2.push(file);
}
}
} else {
fileList2 = fileList;
}
//开始拷贝
that._exportNormal(fileList2, pathTo);
that._exportDuplicate(duplicate2, pathTo);
that._exportImage(pathFrom, pathTo);
that._exportHome(pathFrom, pathTo);
that._exportNavigation(fileList2, duplicate2, pathFrom, pathTo);
}).catch((e) => {
console.error(e);
});
},
/**
* 导出准备
* @param {String} pathFrom
* @param {String} pathTo
* @returns {Promise}
* @private
*/
_toPrepare: function (pathFrom, pathTo) {
const that = this;
return co(function*() {
const fileList = [];
pathFrom += /[\\\/]$/.test(pathFrom) ? 'library/' : '/library/';
//读取文件夹
const [, , files] = mngFolder.readLibraryTree(pathFrom);
if (files.length === 0) {
return;
}
let fileName = '';
//提取文件名
for (let item of files) {
fileName = item.split(/[\\\/]/);
fileList.push([
item,
fileName[fileName.length - 1].replace(/^\d+(\.\d+)?[-_](.*?)$/, '$2')
]);
}
//重名检查
const duplicate = that._checkDuplicate(fileList);
//重名文件单独处理
if (duplicate.length > 0) {
const dups = [];
let message = '以下文件脱离文件夹偏平化时将会重名:\n\n';
for (let j = 0, dup; dup = duplicate[j]; j++) {
message += dup[0][0].replace(/\//g, '\\') + '\n' +
dup[1][0].replace(/\//g, '\\') + '\n\n';
dups.push(dup[0][0], dup[1][0]);
}
message += '点击确认将自动处理(追加额外空格)并继续导出;\n' +
'点击取消将退出导出,您可以在改名后再次重新导出。';
if (yield confirm2(message)) {
return that._toExport(pathFrom, pathTo, fileList, dups);
}
} else {
return that._toExport(pathFrom, pathTo, fileList);
}
}).catch((e) => {
console.error(e);
});
},
/**
* GitHub项目地址
* @private
*/
_githubUrl: '',
/**
* 解析 GitHub url
* @param {String} url
* @returns {Boolean|String}
* @private
*/
_parseGithubUrl: function (url) {
if (url === '') {
alert('导出失败!\n未检测到 “github-url” 配置,请在 config.json 中配置您项目的 github-url');
return false;
}
if (url.indexOf('github.com') === -1) {
alert('导出失败!\n配置完整的 Github 项目 URL\n例如 https://github.com/TevinLi/amWiki');
return false;
}
const urlArr = url.split('github.com')[1].split('/');
if (urlArr.length < 3) {
alert('导出失败!\n请配置完整 Github 项目 URL\n例如 https://github.com/TevinLi/amWiki');
return false;
}
urlArr[2] = urlArr[2].split(/\?#/)[0].split(/\.git/i)[0];
return '/' + urlArr[1] + '/' + urlArr[2] + '/';
},
/**
* 导出为github-wiki
* @param {String} root - 文库项目根目录
* @param {String} outputPath - 输出路径
* @returns {Promise}
* @public
*/
export: function (root, outputPath) {
const that = this;
return co(function* () {
const wikiConfig = mngWiki.getWikiByRoot(root).config;
//检测 GitHub 项目地址
that._githubUrl = that._parseGithubUrl(wikiConfig.githubUrl);
if (!that._githubUrl) {
return;
}
yield that._toPrepare(root, outputPath);
});
}
};
})();
module.exports = exportGithub;

104
build/localServer.js Normal file
View File

@ -0,0 +1,104 @@
/**
* 工作端 - 本地服务器模块
* @author Tevin
*/
const fs = require('fs');
const child_process = require('child_process');
const co = require('../modules/co');
const Sever = require('./server.class');
const mngWiki = require('./manageWiki');
const localServer = (function () {
return {
/**
* 本地 server 实例
* @private
*/
_server: null,
/**
* 启动服务器
* @param {Object} wikis - 文库记录列表的引用
* @param {Number} port - 服务器端口号
* @public
*/
run: function (wikis, port) {
const that = this;
return co(function*() {
if (!that._server) {
that._server = new Sever(wikis, port);
yield that._server.run();
}
});
},
/**
* 浏览当前文档
* @param {String} editPath - 当前文档的路径
* @param {Object} wikis - 文库记录列表的引用
* @returns {Promise}
* @public
*/
browser: function (editPath, wikis) {
const that = this;
return co(function*() {
//判断服务器
if (!that._server) {
if (yield confirm2('本地服务器还未启动,您需要启动服务器么?')) {
that._server = new Sever(wikis);
yield that._server.run();
} else {
return;
}
}
//解析地址
const host = 'http://' + that._server.getLocalIP() + ':' + that._server.getPort();
const wikiId = mngWiki.createWikiId(editPath.split('library')[0]);
let url;
if (editPath.indexOf('$navigation.md') >= 0) {
url = host + '/wiki' + wikiId + '/index.html';
} else {
let filePath = editPath.split('library/')[1];
if (typeof filePath === 'undefined') {
url = host + '/wiki' + wikiId + '/index.html';
} else {
filePath = filePath.replace(/\\/g, '/').replace(/ /g, '%20').replace('.md', '');
url = host + '/wiki' + wikiId + '/index.html?file=' + filePath;
}
}
//呼起默认浏览器打开页面
let cmd;
//windows
if (process.platform === 'win32') {
cmd = 'start';
}
//linux
else if (process.platform === 'linux') {
cmd = 'xdg-open';
}
//mac
else if (process.platform === 'darwin') {
cmd = 'open';
}
child_process.exec(cmd + ' ' + url);
}).catch((e) => {
console.error(e);
});
},
/**
* 关闭 amWiki 索引页显示
* @public
*/
setOffIndex: function () {
this._server.offIndex();
},
/**
* 关闭服务器
* @public
*/
destroy: function () {
this._server && this._server.close();
}
};
})();
module.exports = localServer;

187
build/makeMounts.js Normal file
View File

@ -0,0 +1,187 @@
/**
* 工作端 创建 mounts/*.js 页面挂载数据模块
* @author Tevin
*/
const fs = require('fs');
const mngFolder = require('./manageFolder');
const mngWiki = require('./manageWiki');
const makeMounts = (function () {
return {
/**
* 创建 mounts 数据
* @param {String} editPath
* @param {Boolean} [offMsg] - 是否关闭提示
* @public
*/
make: function (editPath, offMsg) {
const rootPath = mngFolder.getProjectFolder(editPath);
if (!this._checkMountsAllow(rootPath)) {
if (offMsg !== true) {
alert('更新失败!\n配置项“page-mounts”不存在或不为 true请在 config.json 中配置');
}
return;
}
const libPath = rootPath + 'library/';
const [, list,] = mngFolder.readLibraryTree(libPath);
const mountData = this._getMountsData(libPath, rootPath, list);
const mountFiles = this._exportMounts(mountData, rootPath);
this._updateMountLinks(mountFiles, rootPath);
},
/**
* 检查 mounts 创建许可
* @param {String} rootPath
* @returns {Boolean}
* @private
*/
_checkMountsAllow: function (rootPath) {
const wiki = mngWiki.getWikiByRoot(rootPath);
if (!wiki) {
return false;
}
const config = wiki.config;
if (typeof config.pageMounts !== 'undefined') {
return config.pageMounts === true || typeof config.pageMounts === 'object';
} else {
return false;
}
},
/**
* 收集 mounts 数据
* @param {String} libPath
* @param {String} rootPath
* @param {[Object]} list
* @returns {{Array}}
* @private
*/
_getMountsData: function (libPath, rootPath, list) {
const mountData = {};
const timestamp = Date.now();
for (let file of list) {
if (file.type !== 'file') {
continue;
}
let filePath = file.path + '/' + file.name;
let lv1Id = 'm' + mngFolder.getLevel1Id(filePath, libPath);
if (lv1Id.length === 0) {
continue;
}
if (typeof mountData[lv1Id] === 'undefined') {
mountData[lv1Id] = [];
}
mountData[lv1Id].push({
name: file.name,
path: filePath.substr(libPath.length),
content: fs.readFileSync(filePath, 'utf-8'),
timestamp: timestamp
});
}
mountData['home'] = this._getHomeMount(libPath);
mountData['nav'] = this._getNavMount(libPath);
mountData['icon'] = this._getIconsMount(rootPath);
//console.log(mountData['home'].name, mountData['nav'].name, mountData['icon'].name);
mountData['home'].timestamp = mountData['nav'].timestamp = mountData['icon'].timestamp = timestamp;
return mountData;
},
/**
* 获取首页挂载数据
* @param {String} libPath
* @returns {{name: String, path: String, content: String}}
* @private
*/
_getHomeMount: function (libPath) {
const list = mngFolder.readFolder(libPath);
for (let name of list) {
if (/^home[-_].*?\.md$/.test(name) || name === '首页.md') {
const homePath = libPath + name;
return {
name: name,
path: homePath.substr(libPath.length),
content: fs.readFileSync(homePath, 'utf-8'),
}
}
}
},
/**
* 获取导航挂载数据
* @param {String} libPath
* @returns {{name: String, path: String, content: String}}
* @private
*/
_getNavMount: function (libPath) {
const filePath = libPath + '$navigation.md';
return {
name: '$navigation.md',
path: filePath.substr(libPath.length),
content: fs.readFileSync(filePath, 'utf-8')
}
},
/**
* 获取图标挂载数据
* @param {String} rootPath
* @returns {{name: String, path: String, content: String}}
* @private
*/
_getIconsMount: function (rootPath) {
const filePath = rootPath + 'amWiki/images/icons.svg';
return {
name: 'icons.svg',
path: '../' + filePath.substr(rootPath.length),
content: fs.readFileSync(filePath, 'utf-8')
}
},
/**
* 输出 ./mounts/*.js 文件
* @param {{Array}} mountData
* @param {String} rootPath
* @returns {Array}
* @private
*/
_exportMounts: function (mountData, rootPath) {
const mountPath = rootPath + 'amWiki/mounts/';
if (!fs.existsSync(mountPath)) {
fs.mkdirSync(mountPath, 0o777);
} else {
mngFolder.cleanFolder(mountPath);
}
const mountTemplate = 'if(typeof AWPageMounts==\'undefined\'){AWPageMounts={}};' +
'AWPageMounts[\'{{id}}\']={{content}}';
const mountFiles = [];
for (let mountId in mountData) {
if (mountData.hasOwnProperty(mountId)) {
let mountContent = JSON.stringify(mountData[mountId]).replace(/'/g, '\\\'');
mountContent = mountTemplate
.replace('{{id}}', mountId)
.replace('{{content}}', mountContent);
let mountName = mountId + '-' + mountContent.length + '.js';
mountFiles.push(mountName);
fs.writeFileSync(mountPath + mountName, mountContent, 'utf-8');
}
}
return mountFiles;
},
/**
* 更新 index.html 文件的挂载
* @param {Array} mountFiles
* @param {String} rootPath
* @private
*/
_updateMountLinks: function (mountFiles, rootPath) {
let linksHtml = '';
for (let fileName of mountFiles) {
linksHtml += '<script src="amWiki/mounts/' + fileName + '"></script>';
}
let indexSrc = fs.readFileSync(rootPath + 'index.html', 'utf-8');
const mountReg = /<div(.*?)aw-include="mountLinks"(.*?)>([\s\S]*?)<\/div>/;
indexSrc = indexSrc.replace(mountReg, function (m, s1, s2, s3) {
return '<div' + s1 + 'aw-include="mountLinks"' + s2 + '>' +
linksHtml + s3 + '</div>';
});
fs.writeFileSync(rootPath + 'index.html', indexSrc, 'utf-8');
}
};
})();
module.exports = makeMounts;

175
build/makeNavigation.js Normal file
View File

@ -0,0 +1,175 @@
/**
* 工作端 - 创建 $navigation.md 导航文件模块
* @author Tevin
*/
const fs = require('fs');
const mngFolder = require('./manageFolder');
const makeNavigation = (function () {
return {
/**
* 刷新导航
* @param {String} editPath - 当前文档的路径
* @returns {String} library文件夹路径
* @public
*/
refresh: function (editPath) {
const path = editPath.replace(/\\/g, '/').split('library')[0] + 'library/';
const [tree, list] = mngFolder.readLibraryTree(path);
if (!tree) {
return '';
}
this.make(path, tree, list);
return path
},
/**
* 创建 .md 导航文件
* @param {String} path - 文库library文件夹路径
* @param {Object} tree - 目录结构树形数据
* @param {Array} list - 目录结构列表数据
* @public
*/
make: function (path, tree, list) {
//先检查id重复
if (this._checkDuplicateId(tree)) {
return;
}
let errorHas = false;
let markdown = '';
//首页链接输出为 Markdown 文本
for (let fileName in tree) {
if (tree.hasOwnProperty(fileName)) {
if (fileName === '首页.md') {
break;
} else if (/^home[-_].*?\.md$/.test(fileName)) {
const [, path, name] = fileName.match(/^(home[-_])(.*?)\.md$/);
markdown += '\n#### [' + name + '](?file=' + path + name + ')\n';
break;
}
}
}
markdown = (markdown === '') ? '\n#### [首页](?file=首页 "返回首页")\n' : markdown;
//导航数据输出为 Markdown 文本
for (let item of list) {
//检查 id 合法性
if (!this._checkFileId(item.name, item.path)) {
errorHas = true;
continue;
}
//转义文件名中包含的语法符号
item.name = item.name
.replace(/'/g, '&#39;') //转义单引号
.replace(/"/g, '&#34;') //转义双引号
.replace(/\(/g, '&#40;') //转义左圆括号
.replace(/\)/g, '&#41;') //转义右圆括号
.replace(/\[/g, '&#91;') //转义左中括号
.replace(/\]/g, '&#93;'); //转义右中括号
//一级目录
if (item.depth === 0) {
//一级目录不允许文件
if (item.type !== 'folder') {
continue;
}
markdown += '\n##### ' + item.name.match(/^\d+(\.\d+)?[-_](.*?)$/)[2] + '\n';
}
//深目录
else {
//层级缩进
markdown += new Array(item.depth).join(' ');
//文件夹和文件分开处理
if (item.type === 'folder') {
markdown += '- **' + item.name.match(/^\d+(\.\d+)?[-_](.*?)$/)[2] + '**\n';
} else if (item.type === 'file') {
//检查名称合法性
if (!this._checkFileName(item.name, item.path)) {
errorHas = true;
continue;
}
const name = item.name.match(/^\d+(\.\d+)?[-_](.*?)\.md$/)[2];
const path = item.path.split('library/')[1] + '/' + item.name.split('.md')[0];
markdown += '- [' + name + '](?file=' + path + ' "' + name + '")\n';
}
}
}
if (!errorHas) {
fs.writeFileSync(path + '$navigation.md', markdown, 'utf-8');
}
},
/**
* 检查id合法性
* @param {String} name
* @param {String} path
* @returns {Boolean}
* @private
*/
_checkFileId: function (name, path) {
if (/^\d+(\.\d+)?[-_](.*?)$/.test(name)) {
return true
} else {
const text = '不合法的排序ID\n请修改排序ID为整数或浮点数且使用连接符或下划线与具体名称相接\n' +
' at path "library/' + path.split('library/')[1] + '"\n' +
' at file "' + name + '"';
alert(text);
return false;
}
},
/**
* 检查名称合法性
* @param {String} name
* @param {String} path
* @returns {Boolean}
* @private
*/
_checkFileName: function (name, path) {
if (/^\d+(\.\d+)?[-_](.*?)\.md$/.test(name)) {
return true
} else {
const errText = '不合法的文件名!\n请将文件名修改为形如 “排序ID-名称.md” 格式的名称\n' +
' at path "library/' + path.split('library/')[1] + '/"\n' +
' at file "' + name + '"';
alert(errText);
return false;
}
},
/**
* 递归检查(某一目录下的同级文件和文件夹的)ID是否存在重复
* @param {Object} tree - 当前层级的目录结构
* @param {String} path - 当前目录所在路径
* @return {Boolean} 如果返回为true则说明存在重复ID
* @private
*/
_checkDuplicateId: function (tree, path = '') {
let duplicate = false;
let idList = {};
for (let name in tree) {
if (tree.hasOwnProperty(name)) {
if (!idList[name.split(/[-_]/)[0]]) {
idList[name.split(/[-_]/)[0]] = name;
} else {
alert('重复的排序ID\n同级目录下存在相同的排序ID(' + name.split('-')[0] + ')请更改为不同的ID\n' +
' at path "library/' + path + '"\n' +
' at file1 "' + idList[name.split('-')[0]] + '"\n' +
' at file2 "' + name + '"');
duplicate = true;
}
//文件夹
if (tree[name]) {
// duplicate 为真后,不再改变其值
if (duplicate) {
this._checkDuplicateId(tree[name], path + name + '/')
}
// duplicate 为否时,接收子级真假值
else {
duplicate = this._checkDuplicateId(tree[name], path + name + '/');
}
}
}
}
return duplicate;
}
};
})();
module.exports = makeNavigation;

307
build/manageFolder.js Normal file
View File

@ -0,0 +1,307 @@
/**
* 工作端 - 文件夹管理模块
* @author Tevin
*/
const fs = require('fs');
const manageFolder = (function () {
return {
/**
* 判断一个文件夹是否为 amWiki 文库项目
* @param {String} path - 需要判断的文件夹路径
* @returns {Boolean|String} 判断为否时返回 false判断为真时返回项目根目录的路径
* @public
*/
isAmWiki: function (path) {
if (!path && typeof path !== 'string') {
return false;
}
path = path.replace(/\\/g, '/');
path = path.indexOf('config.json') < 0 ? path : path.split('config.json')[0];
path = path.indexOf('index.html') < 0 ? path : path.split('index.html')[0];
//获取 library 路径
path = this.getProjectFolder(path);
if (!path) {
return false;
}
//通过识别文件夹子项来判定
else {
let states = [
fs.existsSync(path + 'library/'),
fs.existsSync(path + 'amWiki/'),
fs.existsSync(path + 'config.json'),
fs.existsSync(path + 'index.html')
];
return states[0] && states[1] && states[2] && states[3] ? path : false;
}
},
/**
* 递归分析指定文件夹下的目录结构
* @param {String} dirPath - 指定要分析的目录
* @param {Number} [depth=0] - 文件夹深度
* @param {Object} [tree={}] - 当前深度结构的树形
* @param {[Object]} [list=[]] - 当前深度结构的列表
* @param {[String]} [files=[]] - 当前深度文件的列表
* @returns {{tree: Object, list: [Object], files: [String]}} - 树形数据列表数据仅文件列表数据
* @private
*/
_listSubFolder: function (dirPath, depth = 0, tree = {}, list = [], files = []) {
try {
let filePath;
for (let fileName of this.readFolder(dirPath)) {
//文件路径
filePath = dirPath + (depth > 0 ? '/' : '') + fileName;
//文件夹及其递归处理
if (this.isFolder(filePath)) {
//读取下一级数据
const [tempTree, tempList, tempFiles] = this._listSubFolder(filePath, depth + 1);
//记录子级的树形
tree[fileName] = tempTree;
//将此文件夹和其所有子级文件(夹)加入结构列表
list.push({
depth: depth,
type: 'folder',
name: fileName,
path: dirPath
}, ...tempList);
//将所有子级文件加入文件列表
files.push(...tempFiles);
}
//第二层以下深度才允许使用文件(第一层仅允许为文件夹)
else {
if (depth > 0) {
//类型为文件时,树形标记为否
tree[fileName] = false;
//将此文件加入结构列表
list.push({
depth: depth,
type: 'file',
name: fileName,
path: dirPath
});
//将此文件加入文件列表
files.push(filePath);
}
}
}
} catch (err) {
//readdirSync 读取失败时仅抛错
console.error(err);
}
return [tree, list, files];
},
/**
* 读取文库library文件夹树形数据
* @param {String} path - 文库library文件夹路径
* @returns {{tree: Object, list: [Object], files: [String]}} - 树形数据列表数据仅文件列表数据
* @public
*/
readLibraryTree: function (path) {
if (!/library[\\\/]$/.test(path)) {
console.warn('The path is not a library.');
return [];
}
const tree = {};
for (let fileName of this.readFolder(path)) {
if (/^home[-_].*?\.md$/.test(fileName) || fileName === '首页.md') {
tree[fileName] = false;
break;
}
}
return this._listSubFolder(path, 0, tree);
},
/**
* fs 同步读取文件夹改进版忽略以点开头的文件
* (比如 Mac .DS_Store 文件)
* @param {String} path
* @param {Boolean} [force] - 是否强制读取文件夹内所有内容
* @returns {Array}
* @public
*/
readFolder: function (path, force) {
let files = [];
try {
if (force === true) {
files = fs.readdirSync(path);
} else {
fs.readdirSync(path).forEach(function (fileName) {
//忽略点开头的文件
if (fileName.indexOf('.') !== 0) {
files.push(fileName);
}
});
}
} catch (e) {}
return files;
},
/**
* 递归清空文件夹
* @param {String} path - 要清空的文件夹
* @param {Boolean} [force] - 是否强制清理文件夹内所有文件
* @public
*/
cleanFolder: function (path, force) {
const list = this.readFolder(path, force);
let path2;
for (let item of list) {
path2 = path.replace(/\\?$/, '/') + item;
//文件夹,递归删除
if (this.isFolder(path2)) {
this.cleanFolder(path2, true);
fs.rmdirSync(path2);
}
//文件
else {
fs.unlinkSync(path2);
}
}
},
/**
* 递归创建文件夹
* @param {String} path - 需要创建的文件夹路径
* @public
*/
createFolder: function (path) {
//先判断父级文件夹是否存在,不存在先创建父级文件夹
const parentPath = this.getParentFolder(path);
if (!fs.existsSync(parentPath)) {
this.createFolder(parentPath); //向上递归创建父级
this.createFolder(path); //创建完父级后再创建本级
}
//如果父级已存在,直接创建本级
else {
if (!fs.existsSync(path)) {
fs.mkdirSync(path, 0o777);
}
}
},
/**
* 深度拷贝一个文件夹到指定位置
* @param {String} from
* @param {String} to
* @public
*/
copyFolder: function (from, to) {
const list = this.readFolder(from);
let from2, to2;
for (let item of list) {
from2 = from + '/' + item;
to2 = to + '/' + item;
if (this.isFolder(from2)) {
this.createFolder(to2);
this.copyFolder(from2, to2);
} else {
fs.createReadStream(from2).pipe(fs.createWriteStream(to2));
}
}
},
/**
* 判断一个地址是否为文件夹
* @param {String} path
* @returns {Boolean}
* @public
*/
isFolder: function (path) {
let state;
try {
state = fs.statSync(path).isDirectory(path);
} catch (e) {
state = false;
}
return state;
},
/**
* 获取项目文件夹
* @param {String} path - 需要计算的文件夹路径
* @return {String|Boolean} 放回当前文库项目的路径如果非文库返回 false
* @public
*/
getProjectFolder: function (path) {
path = this.getLibraryFolder(path);
if (path) {
return path.replace(/library(\\|\/)?$/, '');
} else {
return false;
}
},
/**
* 获取 library 目录路径 (通过反向查找而不是 split 截断)
* @param {String} path - 需要计算的文件夹路径
* @returns {String|Boolean} 返回当前文库 library 目录的路径如果非文库则返回 false
* @public
*/
getLibraryFolder: function (path) {
if (!path) {
return false;
}
//如果当前路径不存在 library 字眼,则判断路径下是否存在 library 文件夹
if (path.indexOf('library') < 0) {
//存在返回拼合路径
if (fs.existsSync(path + '/library/')) {
return path.replace(/\/$/, '') + '/library/';
}
//不存在继续检查上一级
else {
return this.getLibraryFolder(this.getParentFolder(path));
}
}
//存在 library 字眼,反向层层识别,直到 library/ 为止
else {
if (/library\/?$/.test(path)) {
return path.replace(/\/?$/, '/');
} else {
return this.getLibraryFolder(this.getParentFolder(path));
}
}
},
/**
* 获取当前文件()所属上级目录的路径
* @param {String} path - 需要计算的文件夹路径
* @returns {String|Boolean} 父级文件夹路径
* @public
*/
getParentFolder: function (path) {
path = path.replace(/\\/g, '/');
// Win 平台,如果太短直接返回 false
if (/^[a-zA-Z]:/.test(path) && path.length < 3) {
return false;
}
// Linux 平台,如果斜杆数只有一个,返回 false
if (path.replace('/', '').indexOf('/') < 0) {
return false;
}
return path.replace(/\/$/, '').replace(/\/[^\/]+$/, '/');
},
/**
* 获取当前文件()的名称
* @param {String} path - 需要计算的路径
* @return {String} 文件()名称
* @public
*/
getBaseName: function (path) {
return path.replace(/\\/g, '/').replace(/\/$/, '').match(/\/([^\/]*?)$/)[1];
},
/**
* 获取路径对应的一级目录 id
* @param {String} path
* @param {String} [libPath]
* @returns {String}
* @public
*/
getLevel1Id: function (path, libPath) {
if (path.indexOf('library') < 0) {
return '';
}
if (typeof libPath === 'undefined' || libPath < 8) {
let libPath = this.getLibraryFolder(path);
if (!libPath || libPath.length < 8) {
return '';
}
}
return path.substr(libPath.length).split(/[-_]/)[0];
}
};
})();
module.exports = manageFolder;

199
build/manageWiki.js Normal file
View File

@ -0,0 +1,199 @@
/**
* 工作端 - 文库管理模块
* @author Tevin
*/
const fs = require('fs');
const mngFolder = require('./manageFolder');
const tools = require('./tools');
const manageWiki = (function () {
return {
/**
* 文库记录
* @private
*/
_wikis: null,
/**
* 绑定文库记录
* @param {Object} wikis - 文库记录列表的引用
* @public
*/
linkWikis: function (wikis) {
this._wikis = wikis;
},
/**
* 获取所有文库记录
* @returns {Object}
* @public
*/
getWikis: function () {
return this._wikis
},
/**
* 通过 root 地址获取单个文库记录
* @param {String} root
* @returns {Object}
* @public
*/
getWikiByRoot: function (root) {
for (let wId in this._wikis){
if (this._wikis.hasOwnProperty(wId)) {
if (this._wikis[wId].root === root) {
return this._wikis[wId]
}
}
}
return null;
},
/**
* 添加一个文库到记录
* @param {String} root - 文库根目录
* @param {Number} [id] - 文库的计算id
* @public
*/
addWiki: function (root, id = this.createWikiId(root)) {
if (typeof root === 'undefined') {
return;
}
if (typeof this._wikis[id] !== 'undefined') {
//读取配置文件
this.updateWikiConfig();
return;
}
//文库信息
this._wikis[id] = {
//文库id由真实地址计算而来也就是说文库是基于不同地址来管理的
id: id,
//文库根目录地址
root: root,
//文库文档地址
path: root + 'library/',
//弃用标记,当文库被删除或转移走后,原有地址标记为弃用
deprecated: false,
//文件清单md5值用于检测文库目录树变化
treeMD5: '',
//文库配置文件的解析结果
config: null
};
//读取配置文件
this.updateWikiConfig();
},
/**
* 计算文库id
* @param {String} root - 需要计算id的文库根目录
* @returns {Number} 文库id由根目录路径字符串计算而来
* @public
*/
createWikiId: function (root) {
root = root.replace(/\\/g, '/').replace(/\/$/, '');
//累加地址字符串每个字符Unicode值与此字符序号的乘积
let code = 0;
for (let j = 0; j < root.length; j++) {
code += root.charCodeAt(j) * j;
}
//再与地址字符串长度拼合
code = parseInt(root.length + '' + code);
return code;
},
/**
* 判断文库是否加入记录未加入则补录
* @param {String} path - 需要判断的路径
* @public
*/
checkAddWiki: function (path) {
//验证转换 path 为 wiki 根目录
const root = mngFolder.isAmWiki(path);
if (root) {
this.addWiki(root);
}
},
/**
* 检查文库是否仍然有效失效则标记弃用
* @param {Number} wId - 需要判断的文库id
* @public
*/
checkWikiValid: function (wId) {
if (typeof this._wikis[wId] !== 'undefined') {
const root = this._wikis[wId].root;
//当文件夹不存在或者文件夹不为amWiki时标记弃用
//也就是说从atom目录树删除项目并不会标记弃用必须删除文库本地文件和文件夹
this._wikis[wId].deprecated = !fs.existsSync(root) || !mngFolder.isAmWiki(root)
}
},
/**
* 更新文库记录中对应 config.json 的内容
* @public
*/
updateWikiConfig: function () {
for (let wId in this._wikis) {
if (this._wikis.hasOwnProperty(wId)) {
//读取 config
if (!this._wikis[wId].deprecated) {
let configStr = fs.readFileSync(this._wikis[wId].root + 'config.json') || '{}';
try {
this._wikis[wId].config = this.parseConfig(JSON.parse(configStr));
} catch (e) {
console.error('您位于 ' + this._wikis[wId].root +
' 的文库config.json 内容格式不合法,请按照 json 格式书写!');
}
}
//弃用的文库删除 config
else {
this._wikis[wId].config = null;
}
}
}
},
/**
* 重新解析/过滤 config 配置
* @param {Object} config
* @return {Object} config
* @public
*/
parseConfig: function (config) {
//库名称
config.name = typeof config.name === 'undefined' ? 'amWiki轻文库' : config.name + '';
//库版本号
config.version = typeof config.ver === 'undefined' ? 'by Tevin' : config.ver + '';
//logo地址
config.logo = typeof config.logo === 'undefined' ? 'amWiki/images/logo.png' : config.logo + '';
//是否开启接口测试
config.testing = !!config.testing || false;
//设置自定义颜色
config.colour = typeof config.colour === 'undefined' ? '#4296eb' : config.color + '';
//本地页面数据挂载
config.pageMounts = !!(config['page-mounts'] || config.pageMounts) || false;
delete config['page-mounts'];
//项目的 github 地址
config.githubUrl = config['github-url'] || config.githubUrl;
config.githubUrl = typeof config.githubUrl === 'undefined' ? '' : config.githubUrl + '';
delete config['github-url'];
//转接 library 地址
config.libraryPrefix = config['library-prefix'] || config.libraryPrefix;
config.libraryPrefix = typeof config.libraryPrefix === 'undefined' ? '' : config.libraryPrefix + '';
delete config['library-prefix'];
//自定义 css、js 文件
if (tools.isArray(config.imports) && config.imports.length > 0) {
const imports2 = {
js: [],
css: []
};
for (let file of config.imports) {
let type = file.split(/[?#]/)[0].match(/((\.js)|(\.css))$/)[0];
if (type === '.js') {
imports2.js.push(file);
} else if (type === '.css') {
imports2.css.push(file);
}
}
config.imports = imports2;
} else {
delete config.imports;
}
return config;
}
};
})();
module.exports = manageWiki;

295
build/server.class.js Normal file
View File

@ -0,0 +1,295 @@
/**
* 工作端 - 本地服务器类
* @author Tevin
*/
const fs = require('fs');
const util = require('util');
const http = require('http');
const url = require('url');
const os = require('os');
const mngWiki = require('./manageWiki');
const gbk = require('../files/gbk.js').GBK;
//文件类型
const MimeType = {
'txt': 'text/plain',
'html': 'text/html',
'css': 'text/css',
'xml': 'application/xml',
'json': 'application/json',
'js': 'application/javascript',
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'gif': 'image/gif',
'png': 'image/png',
'svg': 'image/svg+xml',
'swf': 'application/x-shockwave-flash',
'tiff': 'image/tiff',
'wav': 'audio/x-wav',
'wma': 'audio/x-ms-wma',
'wmv': 'video/x-ms-wmv'
};
/**
* @class Server
*/
class Server {
/**
* @constructor
* @param {[Object]} wikis
* @param {Number} port=5171
*/
constructor(wikis, port = 5171) {
this._wikis = wikis;
this._port = port;
this._localIP = this.getLocalIP();
this._indexShow = true;
this._nodeServer = http.createServer((req, res) => {
this._parse(req, res);
});
}
/**
* 服务器启动
* @return {Promise}
* @public
*/
run() {
return new Promise((resolve, reject) => {
let portCount = 0;
this._nodeServer
.on('listening', () => {
mngWiki.updateWikiConfig();
console.info('Server running at http://' + this._localIP + ':' + this._port + '/');
resolve();
})
.on('error', (e) => {
if (e.syscall === 'listen' && e.code === 'EADDRINUSE') {
if (portCount < 12) {
console.warn('端口 ' + this._port + ' 已被其他程序占用,尝试端口号+1监听端口 ' + (this._port + 1));
portCount++;
this._nodeServer.listen(++this._port);
} else {
console.error('端口从 ' + (this._port - portCount) + ' 到 ' + this._port +
' 尽皆被占用,请使用其他段位的端口!');
reject();
}
} else {
throw e;
}
});
this._nodeServer.listen(this._port);
});
}
/**
* 获取本地ip
* @returns {String} 本地ip地址
* @public
*/
getLocalIP() {
if (this._localIP) {
return this._localIP;
}
const iFaces = os.networkInterfaces();
let ip = '';
for (let dev in iFaces) {
if (iFaces.hasOwnProperty(dev)) {
iFaces[dev].forEach((details) => {
if (details.family === 'IPv4' && details.address.indexOf(192) >= 0) {
ip = details.address;
}
});
}
}
return ip === '' ? '127.0.0.1' : ip;
}
/**
* 获取当前 server 监听的端口
* @return {Number} 端口号
* @public
*/
getPort() {
return this._port;
}
/**
* 关闭 amWiki 索引页显示
* @public
*/
offIndex() {
this._indexShow = false;
}
/**
* 解析请求
* @param {Object} req - 请求体
* @param {Object} res - 响应体
* @private
*/
_parse(req, res) {
const pathname = url.parse(req.url).pathname;
//文库列表页
if (/^(\/|(\/index(\.html)?))$/.test(pathname)) {
if (this._indexShow) {
return this._renderIndexPage(req, res);
} else {
return Server.page404(req, res, pathname);
}
}
//文库
let wId = '';
let filePath = pathname.replace(/^\/wiki(\d{3,}?)\//g, function (match, $1) {
wId = $1;
return '';
});
//如果文库不存在或已弃用返回404
if (typeof this._wikis[wId] === 'undefined' || this._wikis[wId].deprecated) {
return Server.page404(req, res, pathname);
}
//编码切换
try {
filePath = decodeURI(filePath);
} catch (e) {
filePath = gbk.decode(filePath);
}
//真实地址
const realPath = this._wikis[wId].root + filePath;
//解析文件
try {
fs.exists(realPath, (exists) => {
if (!exists) {
return Server.page404(req, res, pathname);
} else {
const file = fs.createReadStream(realPath);
res.writeHead(200, {
'Content-Type': MimeType[realPath.split('.').pop()] || 'text/plain'
});
file.on('data', res.write.bind(res));
file.on('close', res.end.bind(res));
file.on('error', function (err) {
return Server.page500(req, res, err);
});
}
});
} catch (err) {
return Server.page500(req, res, err);
}
}
/**
* 渲染索引页
* @param {Object} req - 请求体
* @param {Object} res - 响应体
* @private
*/
_renderIndexPage(req, res) {
//样式
let styleTp = 'body{background:#f5f5f5}h1,ul{display:block;margin:0 auto;padding:0;width:960px;max-width:95%}' +
'h1{line-height:100px;text-align:center;font-weight:normal}' +
'ul{display:block;margin-bottom:30px;}' +
'li{display:block;position:relative;min-height:100px;padding:20px;margin-bottom:10px;color:gray;' +
'background:#fff;border-radius:5px}' +
'li>a{display:block;text-decoration:none;}' +
'li strong{display:block;margin-right:150px;padding-bottom:8px;font-size:20px;font-weight:normal;' +
'line-height:38px;color:#333;border-right:#e5e5e5 1px solid;border-bottom:#e5e5e5 1px solid;}' +
'li>a:hover strong{border-bottom:#4296eb 1px solid;}' +
'li strong small{margin-left:4px;padding:0 4px;font-style:normal;font-size:12px;line-height:20px;' +
'color:#fff;vertical-align:text-top;background:#bbbbbb;}' +
'li img{position:absolute;top:50%;right:90px;max-width:120px;max-height:120px;' +
'-webkit-transform:translateY(-50%) translateX(50%);-moz-transform:translateY(-50%) translateX(50%);' +
'-ms-transform:translateY(-50%) translateX(50%);transform:translateY(-50%) translateX(50%)}' +
'li p{margin:0 150px 0 0;padding-top:10px;line-height:30px;word-wrap:break-word;word-break:break-all;' +
'border-right:#e5e5e5 1px solid}' +
'li p span{display:block}li p a,li p b{color:gray}';
//布局
let layoutTp = '<!DOCTYPE html><html><head><meta charset="UTF-8">' +
'<title>amWiki Index Page</title>' +
'<style>{{style}}</style></head><body>' +
'<h1>amWiki Index Page <small>(文库列表索引)</small></h1>' +
'<ul>{{list}}</ul></body></html>';
//列表单项
let itemTp = '<li><a href="{{link}}" target="_blank">' +
'<strong>{{title}}<small>{{version}}</small></strong></a>' +
'<img src="{{logo}}" /><p>{{content}}</p></li>';
//列表
let list = '';
for (let wId in this._wikis) {
if (this._wikis.hasOwnProperty(wId) && !this._wikis[wId].deprecated) {
let wiki = this._wikis[wId];
//文库链接
let link = 'http://' + this.getLocalIP() + ':' + this.getPort() + '/wiki' + wiki.id + '/index.html';
//文库 logo 地址
let logo = wiki.config.logo;
if (logo.indexOf('http') < 0) {
logo = '/wiki' + wiki.id + '/' + logo;
}
//文库详情
let content = '<span>ID: <b>' + wiki.id + '</b></span>' +
'<span>本地位置: <b>' + wiki.root + '</b></span>' +
'<span>本地网址: <b>' + link + '</b></span>';
if (wiki.config.testing) {
content += '<span>测试模块已开启</span>';
}
if (wiki.config['github-url']) {
content += '<span>Github-url: <a href="' + wiki.config['github-url'] + '">' +
wiki.config['github-url'] + '</a></span>';
}
//添加到列表
list += itemTp.replace('{{link}}', link)
.replace('{{title}}', wiki.config.name)
.replace('{{version}}', wiki.config.version)
.replace('{{logo}}', logo)
.replace('{{content}}', content);
}
}
//输出
res.writeHead(200, {
'Content-Type': 'text/html'
});
res.write(layoutTp.replace('{{style}}', styleTp).replace('{{list}}', list));
res.end();
}
/**
* 404未找到页面
* @param {Object} req
* @param {Object} res
* @param {String} path
* @static
*/
static page404(req, res, path) {
res.writeHead(404, {
'Content-Type': 'text/html'
});
res.write('<!doctype html>\n');
res.write('<title>404 Not Found</title>\n');
res.write('<h1>Not Found</h1>');
res.write('<p>The requested URL ' + path + ' was not found on this server.</p>');
res.end();
}
/**
* 500错误页面
* @param {Object} req
* @param {Object} res
* @param {Error} error
* @static
*/
static page500(req, res, error) {
res.writeHead(500, {
'Content-Type': 'text/html'
});
res.write('<!doctype html>\n');
res.write('<title>Internal Server Error</title>\n');
res.write('<h1>Internal Server Error</h1>');
res.write('<pre>' + util.inspect(error) + '</pre>');
res.end();
}
}
module.exports = Server;

21
build/tools.js Normal file
View File

@ -0,0 +1,21 @@
/**
* 工作端 - 公用工具模块
* @author Tevin
*/
const tools = (function () {
return {
/**
* 判断一个对象是否属于数组
* @param {Object} obj - 需要判断的对象
* @return {Boolean}
* @public
*/
isArray: function (obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
}
};
})();
module.exports = tools;

View File

@ -14,8 +14,11 @@
.markdown-body .anchor svg{visibility:hidden;width:20px;height:20px;color:#828282;}
.markdown-body h3 .anchor{position:relative;top:2px;}
.markdown-body h1:hover .anchor svg,.markdown-body h2:hover .anchor svg,.markdown-body h3:hover .anchor svg{visibility:visible;}
.markdown-body :not(.anchor)>img:hover{outline:rgba(0,0,0,.05) 1px solid;box-shadow:1px 1px 1px rgba(0,0,0,.1);}
.markdown-body img:hover{outline:rgba(0,0,0,.05) 1px solid;box-shadow:1px 1px 1px rgba(0,0,0,.1);cursor:pointer;}
.markdown-body img[align="center"]{display:block;margin:0 auto;}
.markdown-body tbody{word-break:initial;}
.markdown-body input[type="checkbox"]{margin-right:5px;vertical-align:middle;cursor:pointer;}
.markdown-body input[type="checkbox"]+label{vertical-align:middle;margin-right:7px;cursor:pointer;}
.markdown-body pre{position:relative;}
.markdown-body pre code{padding-top:0.15em;padding-bottom:0.15em;margin:0 1px;font-size:90%;color:#000;word-wrap:break-word;white-space:pre-wrap;}
.markdown-body pre code.hljs{color:#333;}
@ -29,29 +32,30 @@
.markdown-body .lang-off-js-comment:hover i{color:#999;}
.markdown-body .lang-off-js-comment.on{display:block;color:#333;background:#ccc;}
.markdown-body .lang-off-js-comment.on i{color:#f00;}
.markdown-body mark{color:#e61e1e;background:transparent;}
/* ---------- base ---------- */
body{position:relative;background:#e7e8eb;}
html{height:100%;}
body{position:relative;height:100%;background:#e7e8eb;}
.hidden{display:none !important;}
/* ---------- header ---------- */
.header{width:100%;height:80px;font-family:Microsoft YaHei,STXihei,STHeiti,Verdana,Arial,Helvetica,sans-serif;;background:#fff;border-top:@colour-dark 6px solid;border-bottom:#c3c4c6 1px solid;}
.header .header-inner{height:80px;margin:0 30px;}
.header .logo{float:left;padding-top:20px;}
.header .logo img{float:left;margin-left:5px;max-height:33px;}
.header{width:100%;height:70px;font-family:Microsoft YaHei,STXihei,STHeiti,Verdana,Arial,Helvetica,sans-serif;;background:#fff;border-top:@colour-dark 6px solid;border-bottom:#c3c4c6 1px solid;}
.header .header-inner{margin:0 30px;}
.header .logo{float:left;padding-top:15px;}
.header .logo img{float:left;margin-left:5px;max-height:32px;}
.header .logo i{float:left;display:block;width:5px;height:5px;margin:16px 0 0 3px;background:#666666;border-radius:50%;}
.header .logo b{float:left;max-width:470px;height:33px;padding-left:10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:24px;font-weight:normal;color:#666;}
.header .logo b{float:left;max-width:470px;height:32px;padding-left:10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:24px;font-weight:normal;color:#666;}
.header .logo span{float:left;padding:0 3px;margin:1px 0 0 2px;font-size:xx-small;color:#fff;background:#bbbbbb;}
.header .menu_icon{display:none;}
.header .menu-icon{display:none;}
/* ---------- container ---------- */
.container{position:relative;min-height:500px;margin:30px;overflow:hidden;background:url(../images/menubar_bg.png) repeat-y #fff;border:#c3c4c6 1px solid;border-top:@colour-base 2px solid;}
.container{position:relative;margin:20px 18px 0;background:url(../images/menubar_bg.png) repeat-y #fff;border:#c3c4c6 1px solid;border-top:@colour-base 2px solid;}
/* ---------- menu ---------- */
.container .nav{position:relative;left:auto;top:auto;width:250px;}
/* --- filter --- */
.menu-filter{box-sizing:content-box;position:absolute;top:0;left:0;z-index:10;width:176px;height:37px;padding-left:35px;}
.container .nav{position:absolute;left:0px;top:0px;width:250px;height:100%;}
/* filter */
.menu-filter{box-sizing:content-box;position:absolute;top:0;left:0;z-index:10;width:176px;height:37px;padding-left:35px;border-bottom:1px solid #D9D9D9;}
.menu-filter svg{position:absolute;top:12px;left:12px;width:16px;height:16px;color:@colour-base;}
.menu-filter input{display:block;box-sizing:content-box;width:140px;height:22px;padding:0 25px 0 2px;margin-top:8px;line-height:20px;font-size:14px;background:none;border:none;border-bottom:#e0e0e0 1px dashed;}
.menu-filter input:focus{outline:none;border-bottom:#aaa 1px solid;}
@ -61,44 +65,80 @@ body{position:relative;background:#e7e8eb;}
.menu-filter :-moz-placeholder{color:#ccc;font-size:13px;}
.menu-filter ::-moz-placeholder{color:#ccc;font-size:13px;}
.menu-filter ::-ms-clear{display:none !important;width:0;height:0;}
.menu-filter i{display:block;box-sizing:content-box;position:absolute;top:0px;right:0px;width:23px;padding:2px 10px 0;font-size:25px;text-align:center;color:#ccc;cursor:pointer;}
.menu-filter i{display:block;box-sizing:content-box;position:absolute;top:0px;right:0px;width:23px;padding:0 10px;font-size:25px;text-align:center;color:#ccc;cursor:pointer;}
.menu-filter i:hover{color:#e6660d;}
.menu-filter i.off{display:none;}
.menu-search{display:block;position:absolute;top:0;right:1px;z-index:11;width:39px;height:37px;border-left:1px solid #D9D9D9;cursor:pointer;}
/* search */
.menu-search{display:block;position:absolute;top:0;right:1px;z-index:11;width:39px;height:38px;font-style:normal;border-left:1px solid #D9D9D9;border-bottom:1px solid #D9D9D9;cursor:pointer;}
.menu-search:hover{background-color:#f4f5f9;}
.menu-search svg{position:absolute;top:12px;left:12px;width:16px;height:16px;color:@colour-base;}
/* --- menubar --- */
.menubar{float:left;position:relative;width:250px;height:100%;padding-top:38px;font-family:Microsoft YaHei,STXihei,STHeiti,Verdana,Arial,Helvetica,sans-serif;border-right:1px solid #e7e7eb;}
/* menubar */
.menubar{position:relative;z-index:0;width:250px;height:100%;padding-top:38px;font-family:Microsoft YaHei,STXihei,STHeiti,Verdana,Arial,Helvetica,sans-serif;border-right:1px solid #e7e7eb;}
.menubar h4,.menubar h5{display:block;position:relative;height:37px;margin:-1px 0 0;padding:0 10px 0 30px;font-size:14px;font-weight:normal;color:#333;line-height:37px;text-shadow:#666 1px 0 0;border:1px solid #D9D9D9;border-width:1px 0;cursor:pointer;}
.menubar h4:hover{background-color:#f4f5f9;}
.menubar h5.off{display:none;}
.menubar h5.on{display:block;}
.menubar h4 svg{position:absolute;top:12px;left:12px;width:13px;height:13px;color:#acacac;}
.menubar h4.on svg{color:@colour-base;}
.menubar h4:hover{background-color:#f4f5f9;}
.menubar h4 a{display:block;height:36px;color:#333;text-decoration:none;}
.menubar h4.on a{color:@colour-dark;}
.menubar h5 svg{position:absolute;top:12px;left:12px;width:13px;height:13px;color:#acacac;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);transform:rotate(-90deg);}
.menubar h5.on svg{color:@colour-base;-webkit-transform:rotate(0);-moz-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0);}
.menubar ul{display:none;margin:0;padding:0;list-style:none;}
.menubar ul li{position:relative;}
.menubar ul li{padding-left:23px;}
.menubar .scroller-content > ul > li{padding-left:45px;}
.menubar ul li.off{display:none;}
.menubar ul li a,.menubar ul li > strong{display:block;padding:0 20px 0 0;font-weight:normal;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:30px;font-size:12px;color:#333;text-indent:45px;text-decoration:none;cursor:pointer;-webkit-transition-duration:.3s;-moz-transition-duration:.3s;-ms-transition-duration:.3s;transition-duration:.3s;}
.menubar ul li:first-child a,.menubar ul li:first-child > strong{padding-top:2px;}
.menubar ul li:last-child a,.menubar ul li:last-child > strong{padding-bottom:2px;}
.menubar ul li a:hover,.menubar ul li > strong:hover{background:#f5f5f5;-webkit-transition-duration:0s;-moz-transition-duration:0s;-ms-transition-duration:0s;transition-duration:0s;}
.menubar ul li a.on{color:@colour-dark;background:#f5f5f5;}
.menubar ul li a.on:after{display:block;position:absolute;top:0px;right:0px;width:2px;height:30px;content:" ";background:@colour-base;}
.menubar ul li:first-child a.on:after{height:32px;}
.menubar ul li:last-child a.on:after{height:32px;}
.menubar ul li > a,.menubar ul li > strong{display:block;height:30px;padding:0 55px 0 0;font-weight:normal;white-space:nowrap;line-height:30px;font-size:12px;color:#333;text-decoration:none;cursor:pointer;}
.menubar ul li > a:after,.menubar ul li > strong:after{display:block;position:absolute;left:0;right:0;height:30px;content:" ";-webkit-transition-duration:.3s;-moz-transition-duration:.3s;-ms-transition-duration:.3s;transition-duration:.3s;}
.menubar ul li > a:hover:after,.menubar ul li > strong:hover:after{background:#f5f5f5;-webkit-transition-duration:0s;-moz-transition-duration:0s;-ms-transition-duration:0s;transition-duration:0s;}
.menubar ul li > a > span,.menubar ul li strong > span{float:left;position:relative;z-index:1;}
.menubar ul li > a.on{color:@colour-dark;}
.menubar ul li > a.on:before{display:block;position:absolute;left:0px;z-index:1;width:2px;height:30px;content:" ";background:@colour-base;}
.menubar ul li > a.on:after{background:#f5f5f5;}
.menubar ul li > strong.on{text-shadow:#666 1px 0 0;}
.menubar ul li > strong:before{position:absolute;top:13px;left:32px;width:6px;height:6px;background:#ccc;border-radius:50%;content:" ";}
.menubar ul li:first-child > strong:before{top:15px;}
.menubar ul li > strong:before{position:absolute;z-index:1;width:6px;height:6px;margin:12px 0 0 -12px;background:#acacac;border-radius:50%;content:" ";}
.menubar ul li > strong.on:before{background:@colour-base;}
.menubar ul li li a{text-indent:65px;}
.menubar mark{color:red;background:#f5f5f5;}
.main{position:relative;padding:45px 45px 155px;margin-left:250px;}
.main-sibling{display:none;position:absolute;left:0;bottom:0;width:100%;padding:35px 45px 20px;font-size:14px;border-top:1px solid #e7e7eb;}
/* ---------- scroller ---------- */
body[onselectstart]{cursor:pointer;-moz-user-select:none;-webkit-user-select:none;user-select:none;}
.scroller{height:100%;overflow:hidden;}
.scroller .scroller-inner{box-sizing:content-box;width:100%;height:100%;-webkit-overflow-scrolling:touch;overflow-scrolling:touch;}
.scroller .scroller-inner.on{overflow-y:scroll;}
.scroller .scrollbar{position:absolute;opacity:0;transition-duration:.5s;-moz-user-select:none;-webkit-user-select:none;user-select:none;cursor:pointer;}
.scroller:hover .scrollbar{opacity:1;transition-duration:.3s;}
.scroller .scrollbar.active{opacity:1;}
.scroller .scrollbar.off{opacity:0;}
.scroller .scrollbar div{position:relative;transition-duration:.1s;}
.scroller .scrollbar i{position:absolute;background:rgba(50,50,50,.25);border-radius:2px;transition-duration:.1s;}
.scroller .scrollbar:active{padding:0;}
.scroller .scrollbar:active div{background:rgba(50,50,50,.07);}
.scroller .scrollbar:active i{border-radius:0;}
/* scrollbar-y */
.container .nav .scrollbar.scrollbar-y,.container .nav .scrollbar.scrollbar-y:active{padding-top:38px;padding-bottom:10px;}
.scroller .scrollbar.scrollbar-y{top:0;right:0;width:10px;height:100%;}
.scroller .scrollbar.scrollbar-y div{height:100%;}
.scroller .scrollbar.scrollbar-y i{top:0;left:4px;width:3px;height:100%;}
.scroller .scrollbar.scrollbar-y:active i{left:0;width:10px;}
/* scrollbar-x */
.scroller[data-x-rolling="true"] .scroller-inner.on{overflow-x:scroll;}
.scroller[data-x-rolling="true"] .scroller-content{display:inline-block;position:relative;padding-bottom:10px;white-space:nowrap;min-width:250px;}
.scroller .scrollbar.scrollbar-x{bottom:0;left:0;width:100%;height:10px;padding-right:10px;}
.scroller .scrollbar.scrollbar-x div{width:100%;height:100%;}
.scroller .scrollbar.scrollbar-x i{bottom:0;top:4px;height:3px;width:100%;}
.scroller .scrollbar.scrollbar-x:active i{top:0;height:10px;}
/* ---------- main ---------- */
.main{position:relative;height:100%;margin-left:250px;}
.main .markdown-body{padding:45px;}
/* footnote */
.main .footnote{position:relative;padding-top:35px;border-top:#ddd 1px solid;}
.main .footnote i{position:absolute;top:5px;left:10px;font-size:15px;font-style:normal;color:#999;}
.main .footnote li:focus{background:#EAFFEA;}
.main .footnote svg{display:inline-block;width:14px;height:14px;margin-bottom:-1px;}
.main .footnote-none{width:0;height:0;overflow:hidden;}
/* sibling */
.main-sibling{display:none;width:100%;padding:30px 45px 20px;font-size:14px;border-top:1px solid #e7e7eb;}
.main-sibling.on{display:block;}
.main-sibling p{position:relative;height:22px;}
.main-sibling span{position:absolute;left:0;top:0;}
@ -106,18 +146,46 @@ body{position:relative;background:#e7e8eb;}
.main-sibling a:not([href]){color:#333;}
.main-sibling a:not([href]):hover{text-decoration:none;}
/* ---------- footer ---------- */
footer{position:relative;width:100%;height:15px;}
.signature{position:absolute;right:40px;bottom:17px;color:#999;}
/* ---------- imgsView ---------- */
.imgsv{display:none;position:fixed;top:0;left:0;z-index:999999;width:100%;height:100%;}
.imgsv-background{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.1);cursor:pointer;}
.imgsv-view img{position:absolute;top:48%;left:50%;opacity:0;box-shadow:rgba(0,0,0,.2) 1px 2px 5px;-webkit-transform:translate(-50%,-50%);-moz-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%);-webkit-transition-duration:.5s;-moz-transition-duration:.5s;-ms-transition-duration:.5s;transition-duration:.5s;/*cursor:move;*/
}
.imgsv.on .imgsv-view img{opacity:1;}
.imgsv-panel{position:absolute;left:50%;bottom:-50px;padding:0 10px;line-height:50px;list-style:none;white-space:nowrap;background:rgba(255,255,255,.8);border-top-left-radius:5px;border-top-right-radius:5px;box-shadow:rgba(0,0,0,.2) 0 -1px 5px;-webkit-user-select:none;-moz-user-select:none;user-select:none;-webkit-transform:translateX(-50%);-moz-transform:translateX(-50%);-ms-transform:translateX(-50%);transform:translateX(-50%);-webkit-transition-duration:.5s;-moz-transition-duration:.5s;-ms-transition-duration:.5s;transition-duration:.5s;}
.imgsv.on .imgsv-panel{bottom:0px;}
.imgsv-panel li{display:inline-block;min-width:50px;padding:0 10px;text-align:center;cursor:pointer;}
.imgsv-panel li.off{color:#999;cursor:default;}
.imgsv-panel li:hover{background:rgba(225,225,225,1);}
.imgsv-panel li.off:hover{background:none;}
/* ---------- backTop ---------- */
.back-top{position:fixed;bottom:100px;right:7px;z-index: 9900;height:38px;width:38px;background:#f4f5f9;border:1px solid #ccc;}
.back-icon{display:block;position:relative;width:32px;margin:2px auto;}
.back-icon svg{position:absolute;top:6px;left:5px;width:22px;height:22px;color:#aaa;}
.back-text{display:none;position:absolute;top:0;left:0;z-index:99;margin:4px;font-family:Microsoft YaHei,STXihei,STHeiti,Verdana,Arial,Helvetica,sans-serif;font-size:11px;text-align:center;letter-spacing:2px;line-height:14px;background-color:#f4f5f9;cursor:pointer;}
.back-text:hover{text-decoration:none;}
.back-top:hover .back-icon{border:none;}
.back-top:hover .back-text{display:block;color:#aaa;}
/* ---------- contents ---------- */
aside .contents{position:fixed;bottom:137px;right:7px;z-index:9900;height:38px;width:38px;background:#f4f5f9;border:1px solid #ccc;}
aside .contents .btn{cursor:pointer;}
aside .contents .btn svg{position:absolute;top:7px;left:7px;width:22px;height:22px;color:#aaa;}
aside .contents.hover .btn svg,aside .contents.on .btn svg{display:none;}
aside .contents .btn span{display:none;padding:12px 0;font-family:Microsoft YaHei,STXihei,STHeiti,Verdana,Arial,Helvetica,sans-serif;line-height:14px;text-align:center;color:#aaa;}
aside .contents.hover .btn span,aside .contents.on .btn span{display:block;}
aside .contents-list{display:none;position:absolute;right:36px;bottom:-1px;width:270px;max-height:500px;padding:18px;overflow:hidden;overflow-y:scroll;background:#f4f5f9;border:1px solid #ccc;}
aside .contents.hover .contents-list,aside .contents.on .contents-list{display:block;}
aside .contents-list p{padding-bottom:5px;margin-bottom:8px;border-bottom:#ccc 1px solid;}
blockquote.markdown-contents{padding-left:20px;}
.markdown-contents ol{padding-left:0;list-style:none;}
.markdown-contents ol ol{padding-left:2em;}
.markdown-contents ol li>i{font-style:normal;margin-right:4px;color:#4078c0;}
/* ---------- aside-backTop ---------- */
aside .back-top{position:fixed;bottom:100px;right:7px;z-index: 9900;height:38px;width:38px;background:#f4f5f9;border:1px solid #ccc;}
aside .back-icon{display:block;position:relative;width:32px;margin:2px auto;}
aside .back-icon svg{position:absolute;top:6px;left:5px;width:22px;height:22px;color:#aaa;}
aside .back-text{display:none;position:absolute;top:0;left:0;z-index:99;padding:4px;font-family:Microsoft YaHei,STXihei,STHeiti,Verdana,Arial,Helvetica,sans-serif;font-size:11px;text-align:center;letter-spacing:2px;line-height:14px;background-color:#f4f5f9;cursor:pointer;}
aside .back-text:hover{text-decoration:none;}
aside .back-top:hover .back-icon{border:none;}
aside .back-top:hover .back-text{display:block;color:#aaa;}
/* ---------- footer ---------- */
footer{position:absolute;left:0;bottom:0;width:100%;}
footer .signature{position:absolute;right:20px;bottom:8px;color:#999;}
/* ---------- lowBrowser ---------- */
#lowBrowser{position:fixed;top:0;left:0;z-index:9999999;width:100%;height:100%;}
@ -142,47 +210,63 @@ footer{position:relative;width:100%;height:15px;}
.header .logo i{width:3px;height:3px;margin:9px 0 0 3px;}
.header .logo b{max-width:40%;height:auto;padding-left:7px;font-size:14px;background-size:4px 4px;}
.header .logo span{margin:-5px 0 0 0;-webkit-transform:scale(0.8);-moz-transform:scale(0.8);-ms-transform:scale(0.8);transform:scale(0.8);}
.header .menu_icon{display:block;position:absolute;right:0;top:0;z-index:1;width:40px;height:47px;}
.header .menu_icon svg{position:absolute;top:9px;left:5px;width:28px;height:28px;color:#acacac;}
.header .menu_icon.close svg{top:14px;left:10px;width:18px;height:18px;}
.header .menu-icon{display:block;position:absolute;right:0;top:0;z-index:1;width:40px;height:47px;}
.header .menu-icon svg{position:absolute;top:9px;left:5px;width:28px;height:28px;color:#acacac;}
.header .menu-icon.close svg{top:14px;left:10px;width:18px;height:18px;}
.container{position:relative;margin:50px auto 0;overflow:hidden;background:#fff;border:none;}
.container{position:relative;margin:50px auto 0;background:#fff;border:none;}
/* ---------- menu ---------- */
.container .nav{display:none;position:fixed;left:0;top:50px;z-index:9999;width:100%;height:100%;padding-bottom:30px;background:rgba(0,0,0,0.8);overflow-y:scroll;}
.container .nav{display:none;position:fixed;left:0;top:50px;z-index:9999;width:100%;height:100%;padding-bottom:30px;background:rgba(0,0,0,0.92);overflow-y:scroll;}
.container .nav.on{display:block;}
.menu-filter{width:100%;padding-right:39px;background-color:none;box-sizing:border-box;}
.menu-filter input{width:90%;width:-webkit-calc(100% - 39px);width:-moz-calc(100% - 39px);width:-ms-calc(100% - 39px);width:calc(100% - 39px);color:#fff;background:none;border:none;border-bottom:#999 1px dashed;}
.menu-filter i{right:50px;padding:2px 0 0 10px;}
.menu-search{height:37px;}
.menu-search:hover{background-color:transparent;}
.menu-search:active{background-color:#f4f5f9;}
.menubar{width:100%;height:auto;padding-bottom:50px;border-right:none;}
.menubar i{border-top:1px solid #999;}
.menubar i:hover{background-color:transparent;}
.menubar i:active{background-color:#8c8888;}
.menubar h4,.menubar h5{color:#f5f5f5;border-top:1px solid #999999;border-bottom:none;cursor:pointer;}
.menubar h4,.menubar h5,.menubar ul{max-width:none;}
.menubar h4:hover{background-color:#8c8888;}
.menubar h4 a{color:#f5f5f5;}
.menubar h4.on a{color:#fff;}
.menubar ul li a{color:#f5f5f5;}
.menubar ul li a.on{color:#fff;background:#8c8888;}
.menubar ul li a:hover{color:#fff;background:#8c8888;}
.menubar ul li > a.on{color:#fff;}
.menubar ul li > a:hover{color:#fff;}
.menubar ul li > a.on:before{width:3px;}
.menubar ul li > a.on:after{background:#8c8888;}
.menubar ul li > a:hover:after,.menubar ul li > strong:hover:after{background:none;}
.menubar ul li > strong{color:#fff;}
.menubar ul li > strong:hover{background:none;}
.scroller .scroller-inner{overflow-x:scroll;}
.scroller[data-x-rolling="true"] .scroller-content{min-width:100%;}
.scroller .scrollbar{display:none;}
.main{width:100%;padding:25px 25px 150px;margin-left:0;box-sizing:border-box;}
.main{width:100%;height:auto;margin-left:0;overflow:visible;box-sizing:border-box;}
.main article.markdown-body{padding:25px;}
.main article iframe{border:0;}
.main .search-results{height:auto !important;}
.main-sibling{padding:30px 25px 25px;}
.main-sibling p{height:22px;}
.main-sibling a{padding-right:20px;}
/* ---------- contents ---------- */
aside .contents{bottom:75px;border-radius:50%;}
aside .contents-list{right:40px;width:255px;max-height:310px;}
/* ---------- backTop ---------- */
aside .back-top{border-radius:50%;bottom:32px;}
aside .back-top:hover .back-text{display:none;}
/* ---------- footer ---------- */
footer{position:relative;width:100%;height:0;}
.signature{position:absolute;right:12px;bottom:6px;color:#ccc;}
.signature a{color:#aaa;}
/* ---------- backTop ---------- */
.back-top{border-radius:50%;bottom:32px;}
.back-top:hover .back-text{display:none;}
footer{position:relative;left:0;bottom:0;width:100%;height:0;}
footer .signature{position:absolute;right:12px;bottom:6px;color:#ccc;}
footer .signature a{color:#aaa;}
}

View File

@ -1,5 +1,5 @@
/**
* @desc 文档加载渲染模块
* amWiki Web端 - 文档加载与渲染模块
* @author Tevin
*/
@ -13,23 +13,32 @@
var URL_ENCODE_NAME = 'AMWikiUrlEncode'; //记录url编码的键名
/**
* @class 创建一个文档管理对象
* 文档管理
* @constructor
*/
var Docs = function () {
this.$e = {
win: $(win),
//markdown文档内容容器
view: $('#view')
//markdown 文档内容容器
view: $('#view'),
//网页 title 标签
title: $('title'),
//目录悬浮窗标题
contentsTitle: $('#contentsTitle')
};
this.data = {
//记录页面宽度
pageWidth: 0
};
this.initUrlEncode();
this.initHashEvent();
this._initUrlEncode();
this._initHashEvent();
};
//初始化url编码类型标记
Docs.prototype.initUrlEncode = function () {
/**
* 初始化url编码类型标记
* @private
*/
Docs.prototype._initUrlEncode = function () {
/*
* 由于win与linux采用不同编码保存中文文件名
* 记录浏览器打开当前域名的wiki时服务器中文文件名的编码类型
@ -43,8 +52,11 @@
delete localStorage.urlEcode
};
//修正移动端hash变化时滚动位置
Docs.prototype.initHashEvent = function () {
/**
* 修正移动端hash变化时滚动位置
* @private
*/
Docs.prototype._initHashEvent = function () {
var that = this;
this.data.pageWidth = this.$e.win.width();
this.$e.win.on('resize', function () {
@ -64,11 +76,33 @@
});
};
//设置文档h1、h2、h3描记
Docs.prototype.setTitlesAnchor = function () {
/**
* 转换链接文本
* @param {String} str
* @returns {String}
* @private
*/
Docs.prototype._tramsformLinkText = function (str) {
return str
.replace(/^\s+|\s+$/g, '') //去除首尾空格
.replace(/'/g, '&#39;') //转义单引号
.replace(/"/g, '&#34;') //转义双引号由于双引号无法正确传递给html属性当作为hash时将删除处理
.replace(/\(/g, '&#40;') //转义左圆括号
.replace(/\)/g, '&#41;') //转义右圆括号
.replace(/\[/g, '&#91;') //转义左中括号
.replace(/\]/g, '&#93;'); //转义右中括号
};
/**
* 设置文档h1h2h3描记
* @returns {string}
* @private
*/
Docs.prototype._setTitlesAnchor = function () {
var that = this;
var $titles = null;
var hash = '';
var contentsMd = ''; //提取目录为markdown字符串
if (location.hash && location.hash.length > 1) {
hash = location.hash.split('#')[1];
}
@ -77,11 +111,18 @@
$titles = that.$e.view.find('h1,h2,h3');
$titles.each(function (index, element) {
var $this = $(element);
var text = $.trim($this.text());
var text1 = that._tramsformLinkText($this.text());
var text2 = text1.replace(/&#34;/g, ''); //删除双引号
//提取目录
if ($this.is('h2')) {
contentsMd += '1. [' + text1 + '](#' + text2 + ' "' + text2 + '")\n';
} else if ($this.is('h3')) {
contentsMd += '\t1. [' + text1 + '](#' + text2 + ' "' + text2 + '")\n';
}
//设置描记
$this.prepend(anchorHtml.replace(/\{title\}/g, text));
$this.prepend(anchorHtml.replace(/{title}/g, text2));
//首次打开页面滚动位置修正
if (hash == text) {
if (hash == $this.text().replace(/"/g, '')) {
if (that.data.pageWidth <= 720) {
that.$e.win.scrollTop($this.offset().top - 55);
} else {
@ -89,10 +130,82 @@
}
}
});
if (contentsMd.indexOf('\t') == 0) {
contentsMd = '1. &#12288;\n' + contentsMd;
}
return contentsMd;
};
//设置js注释隐藏
Docs.prototype.setJSCommentDisable = function ($elm) {
/**
* 创建脚注
* @param {String} text
* @returns {String}
* @private
*/
Docs.prototype._setFootnote = function (text) {
var footnotes = [];
var noteReg = /\[\^([ a-zA-Z\d]+)]/g;
var footReg = /\[\^([ a-zA-Z\d]+)]: ?([\S\s]+?)(?=\[\^(?:[ a-zA-Z\d]+)]|\n\n|$)/g;
var templates = $.trim($('#template\\:footnote').text()).split(/[\r\n]+\s*/g);
templates[4] += templates[5] + templates[6] + templates[7] + templates[8];
var html = '';
//提取脚注内容
text = text.replace(footReg, function (match, s1, s2, index) {
var title = '';
s2 = s2.replace(/"(.*?)"\s*$/, function (m, ss1) {
title = ss1;
return '';
});
footnotes.push({
index: index,
note: s1,
content: s2,
title: title,
used: false
});
//从页面上删除底部脚注内容
return '';
});
//将脚注的标记转为序号
text = text.replace(noteReg, function (match, s1) {
for (var i = 0, foot; foot = footnotes[i]; i++) {
if (foot.note == s1) {
foot.used = true;
return templates[0].replace(/{{index}}/g, i + 1 + '').replace('{{title}}', foot.title);
}
}
//当脚注的正文不存在,视标记文本为正文
var length = footnotes.push({
index: 0,
note: s1,
content: s1,
used: true
});
return templates[0].replace(/{{index}}/g, length + '');
});
//生成底部脚注html
if (footnotes.length >= 1) {
for (var i = 0, foot; foot = footnotes[i]; i++) {
if (foot.used) {
html += templates[2]
.replace('{{index}}', i + 1)
.replace('{{content}}', foot.content)
.replace('{{back}}', templates[4].replace('{{index}}', i + 1));
} else {
html += templates[3].replace('{{content}}', foot.content);
}
}
html = templates[1].replace('{{list}}', html);
}
return text + '\n<br>' + html;
};
/**
* 设置js代码块注释显示隐藏
* @param {Object} $elm
* @private
*/
Docs.prototype._setJSCommentDisable = function ($elm) {
var $disBtn = $('<div class="lang-off-js-comment">注<i>/</i></div>');
$disBtn.on('click', function () {
var $this = $(this);
@ -107,8 +220,12 @@
$elm.prepend($disBtn);
};
//解析流程图
Docs.prototype.createFlowChart = function ($elm) {
/**
* 解析流程图
* @param {Object} $elm
* @private
*/
Docs.prototype._createFlowChart = function ($elm) {
var code = $elm.text();
$elm.text('');
var id = 'flowChart' + parseInt(Math.random() * 500);
@ -116,7 +233,7 @@
var chart = flowchart.parse(code);
chart.drawSVG(id, {
'line-width': 1.3,
'line-length': 55,
'line-length': 56,
'line-color': '#666',
'text-margin': 10,
'font-size': 12,
@ -134,30 +251,159 @@
});
};
//编码url
Docs.prototype.encodeUrl = function (path, type) {
var url = '';
/**
* 解析 Markdown 目录
* @param {String} html
* @returns {String}
* @private
*/
Docs.prototype._setTOC = function (html) {
return html.replace(/\[(TOC|MENU)]/g, '<blockquote class="markdown-contents"></blockquote>');
};
/**
* 解析 Markdown 自定义图片大小与对齐方式
* @param {String} html
* @returns {String}
* @private
*/
Docs.prototype._setImgResize = function (html) {
return html.replace(/<img(.*?)src="(.*?)=(\d*[-x×]\d*)(-[lrc])?"/g, function (m, s1, s2, s3, s4) {
var imgHtml = '<img' + s1 + 'src="' + s2 + '"';
var imgSize = s3.split(/[-x×]/);
var align = ({'-l': 'left', '-r': 'right', '-c': 'center'})[s4];
if (imgSize[0]) {
imgHtml += ' width="' + imgSize[0] + '"';
}
if (imgSize[1]) {
imgHtml += ' height="' + imgSize[1] + '"';
}
if (align) {
imgHtml += ' align="' + align + '"';
}
return imgHtml;
});
};
/**
* 解析 markdown 复选框
* @param {String} html
* @returns {String}
* @private
*/
Docs.prototype._setCheckbox = function (html) {
return html.replace(/\[([√×Xx\s\-_])\]\s(.*?)([<\n\r])/g, function (m, s1, s2, s3, index) {
var checkboxHtml = '<input type="checkbox" id="checkbox' + index + '"';
checkboxHtml += /\s/.test(s1) ? '>' : 'checked="true">';
checkboxHtml += '<label for="checkbox' + index + '">' + s2 + '</label>';
return checkboxHtml + s3;
});
};
/**
* 解析 markdown 文字飘红
* @param {String} html
* @returns {String}
* @private
*/
Docs.prototype._setRedText = function (html) {
return html.replace(/==(.*?)==/g, function (m, s1) {
return '<mark>' + s1 + '</mark>';
});
};
/**
* 创建目录
* @param {String} contents - markdown 目录
* @private
*/
Docs.prototype._createContents = function (contents) {
var $contents = $('.markdown-contents').html(marked(contents));
$('blockquote').each(function () {
var $this = $(this);
var $links = $this.find('ol>li>a');
//至少2条链接才视为目录
if ($links.length > 1) {
$this.addClass('markdown-contents');
$contents = $contents.add($this);
}
});
//自带序号的目录,不再额外显示一层序号
$contents.find('ol').each(function () {
var $this = $(this);
var $links = $this.children('li').children('a');
var text1 = $links.eq(0).text(),
text2 = $links.eq(1).text();
var conditions = [
//普通数字类型
/^[\(]?(\d+\.?)+[^\d]{2,}/,
//汉字序号类型
/^[\((【第]?[一二三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+/,
//英文序号类型
/^(chapter)|(section)|(part)|(step)/i,
//罗马序号类型
/^[ⅡⅢⅣIⅥⅦⅧⅨⅪⅫLCDM]+\.?/
];
for (var i = 0, cond; cond = conditions[i]; i++) {
if (cond.test(text1) && cond.test(text2)) {
$this.addClass('unindex');
break;
}
}
});
//没写序号的目录,创建序号
var setIndex = function ($elm, index1) {
if ($elm.length == 0) {
return;
}
$elm.children('li').each(function (index2) {
var $this = $(this);
if (!$elm.hasClass('unindex')) {
var index = typeof index1 == 'number' ? (index1 + 1) + '.' + (index2 + 1): (index2 + 1) + '.';
$this.prepend('<i>' + index + '</i>');
}
setIndex($this.children('ol'), index2);
});
};
$contents.children('ol').each(function () {
setIndex($(this));
});
};
/**
* 编码 url
* 由于服务器可能存在 GBK UTF-8 两种编码中文路径编码不对需要切换才能访问
* @param {String} path
* @param {String} type - 是否需要编码类型反转 GBKUTF-8切换
* @returns {String}
* @private
*/
Docs.prototype._encodeUrl = function (path, type) {
var url = 'library/';
if (typeof AWConfig.libraryPrefix == 'string' && AWConfig.libraryPrefix.length > 0) {
url = AWConfig.libraryPrefix.replace(/\\/g, '/').replace(/\/?&/, '/');
}
var paths = [];
//正常编码
if (type == 'normal') {
if (localStorage[URL_ENCODE_NAME] == 'utf8') {
url = 'library/' + encodeURI(path);
url += encodeURI(path);
} else if (localStorage[URL_ENCODE_NAME] == 'gbk') {
paths = path.split('/');
url = 'library/' + GBK.encode(paths[0]);
url += paths[1] ? '/' + GBK.encode(paths[1]) : '';
url += paths[2] ? '/' + GBK.encode(paths[2]) : '';
paths = path.split('/').map(function (path) {
return GBK.encode(path);
});
url += paths.join('/');
}
}
//反转编码
else if (type == 'reverse') {
if (localStorage[URL_ENCODE_NAME] == 'utf8') {
paths = path.split('/');
url = 'library/' + GBK.encode(paths[0]);
url += paths[1] ? '/' + GBK.encode(paths[1]) : '';
url += paths[2] ? '/' + GBK.encode(paths[2]) : '';
paths = path.split('/').map(function (path) {
return GBK.encode(path);
});
url += paths.join('/');
} else if (localStorage[URL_ENCODE_NAME] == 'gbk') {
url = 'library/' + encodeURI(path);
url += encodeURI(path);
}
}
url += '.md?t=' + (new Date()).getTime();
@ -165,38 +411,66 @@
};
/**
* @desc 渲染文档
* @param content {string} - 需要渲染的文档内容
* 渲染文档
* @param {String} content - 需要渲染的文档内容
* @public
*/
Docs.prototype.renderDoc = function (content) {
var that = this;
var html = '';
this.cleanView();
this.$e.view
.html(marked(content))
.find('pre code').each(function (i, element) {
var $elm = $(element);
var className = $elm.attr('class') || '';
//流程图
if (className.indexOf('lang-flow') >= 0) {
that.createFlowChart($elm);
}
//语法高亮
else if (className.indexOf('lang') >= 0) {
hljs.highlightBlock(element);
}
//js注释开关
className = $elm.attr('class') || '';
if (className.indexOf('javascript') >= 0) {
that.setJSCommentDisable($elm);
}
});
//增加"\"符转义功能
content = content.replace(/\\(.)/g, function (m, s1) {
return '&#' + s1.charCodeAt(0) + ';';
});
//创建脚注
content = this._setFootnote(content);
//编译 markdown
html = marked(content);
//创建目录标记,和悬浮窗格式统一
html = this._setTOC(html);
//自定义图片大小与对齐方式
html = this._setImgResize(html);
//复选框
html = this._setCheckbox(html);
//文字飘红
html = this._setRedText(html);
//填充到页面
this.$e.view.html(html);
//功能化代码块
this.$e.view.find('pre code').each(function (i, element) {
var $elm = $(element);
var className = $elm.attr('class') || '';
//创建流程图
if (className.indexOf('lang-flow') >= 0) {
that._createFlowChart($elm);
}
//创建语法高亮
else if (className.indexOf('lang') >= 0) {
hljs.highlightBlock(element);
}
//创建js注释开关
className = $elm.attr('class') || '';
if (className.indexOf('javascript') >= 0) {
that._setJSCommentDisable($elm);
}
});
//设置网页title
$('title').text(this.$e.view.find('h1').eq(0).text());
//设置描点
this.setTitlesAnchor();
var title = this.$e.view.find('h1').eq(0).text();
this.$e.title.text(title);
this.$e.contentsTitle.text(title).attr('href', '#' + title.replace(/"/g, ''));
//创建描点
var contents = this._setTitlesAnchor();
//创建目录
this._createContents(contents);
};
//读取文档
/**
* 读取文档
* @param {String} url
* @param {Function} callback
* @public
*/
Docs.prototype.getDoc = function (url, callback) {
var that = this;
var ajaxData = {
@ -220,23 +494,21 @@
};
/**
* @callback loadPageCallback
* @param type {string} - 加载页面最终状态successerror 两种
* @param content {string} - 加载页面成功时传递加载的内容
*/
/**
* @desc 加载指定页面
* @param path {string} - 页面资源地址
* @param callback {loadPageCallback} - 加载完成后的回调
* 加载指定页面
* @param {String} path - 页面资源地址
* @param {Function} callback - 加载完成后的回调包含参数
* type - 加载页面最终状态successerror 两种
* content - 加载页面成功时传递加载的内容
* @public
*/
Docs.prototype.loadPage = function (path, callback) {
//console.log(path);
var that = this;
var url = this.encodeUrl(path, 'normal');
var url = this._encodeUrl(path, 'normal');
this.getDoc(url, function (type, data) {
if (type == 'fail') {
//如果第一失败转换url编码类型后发送第二次请求
var url = that.encodeUrl(path, 'reverse');
var url = that._encodeUrl(path, 'reverse');
that.getDoc(url, function (type) {
//第二次仍然失败,视为打开文档失败
if (type == 'fail') {
@ -255,7 +527,8 @@
};
/**
* @desc 清理页面
* 清理页面
* @public
*/
Docs.prototype.cleanView = function () {
this.$e.view.find('.lang-off-js-comment').off('click');

155
files/amWiki.imgsView.js Normal file
View File

@ -0,0 +1,155 @@
/**
* amWiki Web端 - 图片查看模块
* @author Tevin
*/
;(function (win, $) {
'use strict';
/**
* 图片查看器
* @param {Element} _this
* @constructor
*/
var ImgsView = function (_this) {
this.$e = {
container: $(_this), //文档主容器
imgList: null, //页面图片列表
imgsView: $('#imgsView'), //图片弹窗层
imgsViewInner: null, //图片容器
btnPrev: null,
btnNext: null
};
this._data = {
winW: 0,
winH: 0,
curIndex: -1
};
this._init();
};
/**
* 初始化
* @private
*/
ImgsView.prototype._init = function () {
var that = this;
this.$e.imgsViewInner = this.$e.imgsView.find('#imgsViewInner');
this.$e.btnPrev = this.$e.imgsView.find('.prev');
this.$e.btnNext = this.$e.imgsView.find('.next');
this._data.winW = $(win).width();
this._data.winH = $(win).height();
this.$e.container.on('click', function (e) {
if (e.target.tagName.toLowerCase() == 'img') {
//抓取图片列表
that.$e.imgList = that.$e.container.find('img');
that._data.curIndex = that.$e.imgList.index(e.target);
that.open();
}
});
this.$e.imgsView.children('.imgsv-background').on('click', function () {
that.close();
});
this.$e.imgsView.find('.original').on('click', function () {
that.resizeAs('org');
});
this.$e.imgsView.find('.suit').on('click', function () {
that.resizeAs('suit');
});
this.$e.imgsView.find('.prev').on('click', function () {
var $this = $(this);
if (!$this.hasClass('off')) {
that.playTo(that._data.curIndex - 1);
}
});
this.$e.imgsView.find('.next').on('click', function () {
var $this = $(this);
if (!$this.hasClass('off')) {
that.playTo(that._data.curIndex + 1);
}
});
};
/**
* 显示图片浏览弹层
* @public
*/
ImgsView.prototype.open = function () {
var that = this;
this.playTo(this._data.curIndex);
this.$e.imgsView.fadeIn(80);
setTimeout(function () {
that.$e.imgsView.addClass('on');
}, 50);
};
/**
* 关闭图片浏览弹层
* @public
*/
ImgsView.prototype.close = function () {
var that = this;
this.$e.imgsView.removeClass('on');
setTimeout(function () {
that.$e.imgsView.fadeOut(50);
}, 200);
that.$e.imgList = null;
that._data.curIndex = -1;
};
/**
* 切换图片
* @param {Number} index
* @public
*/
ImgsView.prototype.playTo = function (index) {
if (index == 0) {
this.$e.btnPrev.addClass('off');
} else {
this.$e.btnPrev.removeClass('off');
}
if (index == this.$e.imgList.length - 1) {
this.$e.btnNext.addClass('off');
} else {
this.$e.btnNext.removeClass('off');
}
this.$e.imgCur = this.$e.imgList.eq(index).clone().removeAttr('align');
this.resizeAs('suit');
this.$e.imgsViewInner.html(this.$e.imgCur);
this._data.curIndex = index;
};
/**
* 设置图片尺寸
* @param {String} type - 尺寸模式suit 适合 org 原大小
* @public
*/
ImgsView.prototype.resizeAs = function (type) {
if (type == 'suit') {
this.$e.imgCur.removeAttr('width').removeAttr('height');
if (this.$e.imgCur[0].naturalWidth > this._data.winW - 60) {
this.$e.imgCur.css('max-width', this._data.winW - 60);
}
if (this.$e.imgCur[0].naturalHeight > this._data.winH - 60) {
this.$e.imgCur.css('max-height', this._data.winH - 60);
}
} else if (type == 'org') {
this.$e.imgCur.removeAttr('style');
this.$e.imgCur.attr({
'width': this.$e.imgCur[0].naturalWidth,
'height': this.$e.imgCur[0].naturalHeight
});
}
};
//注册到 jQuery
$.extend($.fn, {
imgsView: function () {
return this.each(function () {
return new ImgsView(this);
});
}
});
})(window, jQuery);

View File

@ -1,5 +1,5 @@
/**
* @desc amWiki Web端·主执行模块
* amWiki Web端 - 入口模块
* @author Tevin
* @see {@link https://github.com/TevinLi/amWiki}
* @license MIT - Released under the MIT license.
@ -9,6 +9,10 @@ $(function () {
'use strict';
/*
引入
*/
//工具集
var tools = window.tools;
//文档管理器
@ -24,21 +28,46 @@ $(function () {
//是否支持history.state的API (IE9不支持)
var HISTORY_STATE = 'pushState' in history;
//页面基本显示与操作
var $menuBar = $('#menuBar');
var pageBase = function () {
//菜单折叠
$menuBar.on('click', 'h5', function () {
var $this = $(this),
$next = $this.next('ul');
if ($this.hasClass('on')) {
$this.removeClass('on');
$next.slideUp(200);
/*
页面基本
*/
//页面元素
var $win = $(window),
$body = $('body'),
$menuIcon = $('#menuIcon'), //顶部折叠显示导航按钮
$container = $('#container'), //页面主容器
$nav = $('#nav'), //左侧导航
$menuBar = $('#menuBar'), //左侧导航内容
$filter = $('#menuFilter'),
$filterClean = $filter.next('i'),
$main = $('#main'),
$mainInner = $main.children('.main-inner'),
$mainSibling = $('#mainSibling'), //其他文章
$contents = $('#contents'); //目录
//是否为移动端
var isMobi = window.isMobi = (function () {
var winW = $win.width();
var threshold = 720;
var onResize = function () {
winW = $win.width();
if (winW <= threshold) {
$container.removeAttr('style');
} else {
$this.addClass('on');
$next.slideDown(200);
$container.height($win.height() - 70 - 15 - 20 * 2);
}
});
};
onResize();
$win.on('resize', onResize);
return function () {
return winW <= threshold;
};
})();
//页面基本显示与操作
(function () {
//菜单折叠
$menuBar.on('click', 'h4', function () {
var $this = $(this);
if (!$this.hasClass('on')) {
@ -47,90 +76,76 @@ $(function () {
}
$menuBar.find('h5').removeClass('on').next('ul').hide();
});
$menuBar.on('click', 'h5', function () {
var $this = $(this),
$next = $this.next('ul');
if ($this.hasClass('on')) {
$this.removeClass('on');
$next.slideUp(200, function () {
$menuBar.trigger('scrollbar');
});
} else {
$this.addClass('on');
$next.slideDown(200, function () {
$menuBar.trigger('scrollbar');
});
}
});
$menuBar.on('click', 'strong', function () {
var $this = $(this),
$next = $this.next('ul');
if ($this.hasClass('on')) {
$this.removeClass('on');
$next.slideUp(200);
$next.slideUp(200, function () {
$menuBar.trigger('scrollbar');
});
} else {
$this.addClass('on');
$next.slideDown(200);
$next.slideDown(200, function () {
$menuBar.trigger('scrollbar');
});
}
});
//展开折叠所有导航栏位按钮
/*$('.menu-fold').on('click', function () {
var $this = $(this);
if ($this.hasClass('on')) {
$this.removeClass('on').find('use').attr('xlink:href', '#navFolder1');
$menuBar.find('h5').removeClass('on').next('ul').hide();
} else {
$this.addClass('on').find('use').attr('xlink:href', '#navFolder2');
$menuBar.find('h5').addClass('on').next('ul').show();
}
});*/
//响应式菜单
var $nav = $('.nav'),
$menuIcon = $('.menu_icon');
$menuIcon.on('click', function () {
var $this = $(this);
if ($this.hasClass('close')) {
$this.removeClass('close');
$menuIcon.find('use').attr('xlink:href', '#icon:navStart');
$this.removeClass('close')
.find('use').attr('xlink:href', '#icon:navStart');
$nav.removeClass('on');
} else {
$this.addClass('close');
$menuIcon.find('use').attr('xlink:href', '#icon:navClose');
$this.addClass('close')
.find('use').attr('xlink:href', '#icon:navClose');
$nav.addClass('on');
}
});
$menuBar.on('click', 'li a', function () {
$nav.on('navchange searchon searchoff', function (e) {
$menuIcon.removeClass('close')
.find('use').attr('xlink:href', '#icon:navStart');
$nav.removeClass('on');
});
$menuBar.on('click', 'h4', function () {
$nav.removeClass('on');
});
//页面筛选
var $filter = $('#menuFilter');
var $filterClean = $filter.next('i');
$filter.on('blur change input propertychange', function () {
var value = $filter.val();
if (value != '') {
//筛选操作
$filter.on('input propertychange input2', function () {
var value = $filter.val().replace(/([\(\)\[\]\^\$\+])/g, '\\$1');
var valReg = new RegExp('(' + $.trim($filter.val()).split(/[ ,]/).join('|') + ')', 'ig');
if (value != '' && !/^\s$/g.test(value)) {
$filterClean.removeClass('off');
$menuBar.find('h5').removeClass('on').next('ul').hide();
$menuBar.find('a').each(function () {
var $this = $(this);
if ($this.text().indexOf(value) >= 0) {
$this.html($this.text().replace(value, '<mark>' + value + '</mark>'));
var $prev = $this.parent().removeClass('off').parent().show().prev();
//一级目录
if ($prev.is('h5')) {
$prev.addClass('on');
}
//二级目录
else if ($prev.is('strong')) {
$prev.addClass('on').parent().removeClass('off').parent().show().prev().addClass('on');
}
} else {
if ($this.find('mark').length > 0) {
$this.text($this.text());
}
$this.parent().addClass('off');
}
$menuBar.find('h5').each(function () {
filterNav('filter', valReg, $(this));
});
storage.setStates('navFilterKey', value);
} else {
$filterClean.addClass('off');
$menuBar.find('a').each(function () {
var $this = $(this);
if ($this.find('mark').length > 0) {
$this.text($this.text());
}
$menuBar.find('h5').each(function () {
filterNav('open', null, $(this));
});
$menuBar.find('a').parent().removeClass('off');
storage.setStates('navFilterKey');
}
$menuBar.trigger('scrollbar');
});
//清空筛选
$filterClean.on('click', function () {
$filter.val('').trigger('change');
$filter.val('').trigger('input2');
});
//显示svg图标
if (sessionStorage['AMWikiIconsSvg']) {
@ -139,12 +154,168 @@ $(function () {
$.get('amWiki/images/icons.svg', function (svg) {
sessionStorage['AMWikiIconsSvg'] = svg;
$('#svgSymbols').append(svg);
}, 'text');
}, 'text').fail(function () {
if (typeof AWPageMounts != 'undefined') {
sessionStorage['AMWikiIconsSvg'] = AWPageMounts['icon'].content;
$('#svgSymbols').append(AWPageMounts['icon'].content);
}
});
}
//目录悬浮窗展开折叠
$contents.children('.btn').on('click', function (e) {
$contents.toggleClass('on').removeClass('hover');
});
$contents.hover(function () {
$contents.addClass('hover');
}, function () {
$contents.removeClass('hover');
});
//开启滚动条
$('.scroller').scrollbar();
$('#backTop').on('click', function () {
$mainInner.scrollTop(0);
});
//图片放大
$main.imgsView();
//全局点击
$(document).on('click', function (e) {
var $tag = $(e.target);
//移动端
if (isMobi()) {
//折叠目录悬浮窗
if ($tag.closest('#contents').length == 0) {
$contents.removeClass('on').removeClass('on');
}
}
});
})();
/*
业务操作函数
*/
/**
* 向下递归进行导航筛选
* 当类型为筛选时必定有正则
* 当文件夹匹配时其所属链接和所有子级全部显示且显示匹配
* 当文件夹不匹配时其所属链接和当前子级仅显示匹配隐藏不匹配的项下一级继续筛选
* 当类型为打开时所属链接和子级一律全部显示不隐藏
* 如果有正则显示当前匹配
* 如果无正则清除匹配
* @param {String} type - 筛选类型 filter / open 两个值
* @param {regexp} valReg - 过滤筛选的正则
* @param {Object} $title - jquery 对象标题-列表DOM结构中的标题
*/
var filterNav = function (type, valReg, $title) {
var $ul = $title.next('ul');
//因为一级文件夹和子级文件夹DOM结构不同所以区分对待
// 显示上strong 带 on 加粗显示h5 加 off 隐藏
// 文本操作使用变量 $span其他操作使用变量 $title
var $span = $title.find('span');
//当类型为筛选时
if (type == 'filter' && valReg) {
//当文件夹标题匹配时
if (valReg.test($span.text())) {
$span.html($span.text().replace(valReg, '<mark>$1</mark>'));
$title.addClass('on').removeClass('off');
//所属链接全部显示,且显示匹配
$ul.show().find('> li > a').each(function () {
var $this = $(this);
var $span2 = $this.find('span');
$span2.html($span2.text().replace(valReg, '<mark>$1</mark>'));
$this.parent().removeClass('off');
});
//父级显示
showNavParents($title);
//下一级筛选类型更改,文件和文件夹全部显示,且显示匹配
$ul.find('> li > strong').each(function () {
filterNav('open', valReg, $(this));
});
}
//当文件夹标题不匹配时
else {
$span.text($span.text());
$title.removeClass('on');
//隐藏父级或隐藏h5
if ($title.is('h5')) {
$title.parent().addClass('off');
} else {
$title.addClass('off');
}
//所属链接仅显示匹配的
$ul.hide().find('> li > a').each(function () {
var $this = $(this);
var $span2 = $this.find('span');
if (valReg.test($this.text())) {
$span2.html($span2.text().replace(valReg, '<mark>$1</mark>'));
$this.parent().removeClass('off');
//存在匹配时父级才显示
showNavParents($ul.show().prev());
} else {
$span2.text($span2.text());
$this.parent().addClass('off');
}
});
//下一级继续完全筛选
$ul.find('> li > strong').each(function () {
filterNav('filter', valReg, $(this));
});
}
}
//当类型为打开,显示全部链接和文件夹
else if (type == 'open') {
$title.removeClass('off');
if ($title.hasClass('on')) {
$ul.show();
} else {
$ul.hide();
}
//当存在正则时,显示匹配
if (!!valReg) {
$span.html($span.text().replace(valReg, '<mark>$1</mark>'));
if (valReg.test($span.text())) {
$ul.show(); //当文件夹名称命中,展开文件夹
}
$ul.find('> li > a').each(function () {
var $this = $(this);
var $span2 = $this.find('span');
$span2.html($span2.text().replace(valReg, '<mark>$1</mark>'));
if (valReg.test($this.text())) {
$ul.show(); //当链接名称命中,展开文件夹
}
$this.parent().removeClass('off');
});
//父级显示
showNavParents($title);
//下一级继续以相同类型显示
$ul.find('> li > strong').each(function () {
filterNav('open', valReg, $(this));
});
}
//当正则不存在,显示所有本级和子级、清除匹配
else {
$span.text($span.text());
$ul.find('> li > a').each(function () {
var $span2 = $(this).find('span');
$span2.text($span2.text());
});
$ul.children('li').removeClass('off').children('strong').each(function () {
filterNav('open', null, $(this));
});
}
}
};
//向上递归显示父级
var showNavParents = function ($title) {
$title.addClass('on').removeClass('off');
//向上显示直到一级目录
if (!$title.is('h5')) {
var $prev2 = $title.parent().removeClass('off').parent().show().prev();
showNavParents($prev2);
}
};
//改变底部上下篇目
var $mainSibling = $('#mainSibling');
var changeSibling = function ($item) {
//如果未传导航项进来,隐藏上下篇目栏位
if (!$item) {
@ -177,12 +348,14 @@ $(function () {
};
setSiblingNav(0, getDocLink('prev', $item));
setSiblingNav(1, getDocLink('next', $item));
$mainSibling.addClass('on');
if (testing && !testing.isOpen()) {
$mainSibling.addClass('on');
}
};
//改变导航显示
var changeNav = function (path) {
if (path == '首页') {
if (/^home[-_].*?/.test(path) || path == '首页') {
$menuBar.find('h4').addClass('on');
$menuBar.find('a').removeClass('on');
changeSibling(null);
@ -195,10 +368,9 @@ $(function () {
hsLink = true;
//本层加高亮
var $prev = $this.addClass('on').parent().parent().show().prev().addClass('on');
//如果本层处于第二层,对应的第一层加高亮
if ($prev[0].tagName.toLowerCase() == 'strong') {
$prev.parent().parent().show().prev().addClass('on');
}
//父级高亮
showNavParents($prev);
//改变上下篇切换
changeSibling($this.parent());
} else {
$this.removeClass('on');
@ -208,78 +380,107 @@ $(function () {
$menuBar.find('h4').removeClass('on');
}
}
curPath = path;
$menuBar.trigger('scrollbar');
};
//返回首页
var backHome = function () {
docs.loadPage(homePage.path, function (state, content) {
if (state == 'success') {
changeNav(homePage.path);
docs.renderDoc(content);
storage.saveDoc(homePage.path, content);
$main.trigger('scrollbar');
}
});
if (HISTORY_STATE) {
history.replaceState({path: homePage.path}, '', homePage.url);
}
};
//改变页面
var changePage = function (path, withOutPushState) {
var changePage = function (path, withOutPushState, callback) {
//第一步,从本地缓存读取并渲染页面
var localDoc = storage.read(path);
docs.renderDoc(localDoc);
testing && testing.crawlContent();
$main.trigger('scrollbar');
$mainInner.scrollTop(0); //返回顶部
//更新history记录
if (!withOutPushState && HISTORY_STATE) {
history.pushState({path: path}, '', '?file=' + path);
var path2 = path.replace(/&/g, '%26'); //对带 & 符号的地址特殊处理
history.pushState({path: path}, '', '?file=' + path2);
}
//第二步,加载服务器上的文档资源,如果有更新重新渲染
docs.loadPage(path, function (state, content) {
//读取服务器文档失败
//读取服务器文档失败
if (state == 'error') {
//如果本地缓存为空,且服务器文档读取失败,跳回首页
//如果本地缓存为空
if (localDoc == '') {
docs.loadPage('首页', function (state, content) {
if (state == 'success') {
docs.renderDoc(content);
storage.saveDoc('首页', content);
}
});
if (HISTORY_STATE) {
history.replaceState({path: '首页'}, '', '?file=首页');
}
backHome();
}
//如果本地缓存不为空,但服务器文档读取失败
//如果本地缓存不为空
else {
//记录文档打开数
storage.increaseOpenedCount(path);
callback && callback();
}
}
//读取服务器文档成功
//读取服务器文档成功
else if (state == 'success') {
//如果服务器文档有更新,更新本地缓存、重新渲染页面、重新判断接口测试
if (content != localDoc) {
docs.renderDoc(content);
storage.saveDoc(path, content);
testing && testing.crawlContent();
$main.trigger('scrollbar');
}
//如果服务器文档与本地缓存一致,不进行任何操作
else {}
else {
}
//记录文档打开数
storage.increaseOpenedCount(path);
callback && callback();
}
});
};
//读取目录导航
var homePage = {};
var loadNav = function (callback) {
$.get('library/$navigation.md?t=' + Date.now(), function (data) {
$menuBar.html(marked(data));
$menuBar
.find('h4').prepend('<svg><use xlink:href="#icon:navHome"></use></svg>').end()
.find('h5').prepend('<svg><use xlink:href="#icon:navArrow"></use></svg>');
var fillNav = function (data) {
$menuBar.find('.scroller-content').html(marked(data));
//首页
var menuBarHome = $menuBar.find('h4');
homePage.text = menuBarHome.text();
homePage.url = menuBarHome.find('a').attr('href');
homePage.path = homePage.url.split('file=')[1];
menuBarHome.prepend('<svg><use xlink:href="#icon:navHome"></use></svg>');
//列表
$menuBar.find('h5').each(function () {
var $this = $(this);
$this.html('<svg><use xlink:href="#icon:navArrow"></use></svg><span>' + $this.text() + '</span>')
});
$menuBar.trigger('scrollbar');
var pathList = [];
//支持history api时改变默认事件导航不再跳转页面
$menuBar.find('a').each(function () {
var $this = $(this);
$this.html('<span>' + $this.text() + '</span>');
if (HISTORY_STATE) {
var $this = $(this);
var path = $this.attr('href').split('file=')[1];
pathList.push(path);
$this.on('click', function () {
search.displayBox('off'); //关闭搜索面板
changeNav(path);
changePage(path);
$this.trigger('navchange');
return false;
});
}
});
//上下翻页不再跳页面
$mainSibling.find('a').on('click', function () {
if (HISTORY_STATE) {
var $this = $(this);
@ -288,22 +489,165 @@ $(function () {
var path = href.split('file=')[1];
changeNav(path);
changePage(path);
$this.trigger('navchange');
}
return false;
}
});
$menuBar.find('strong').each(function () {
var $this = $(this);
$this.html('<span>' + $this.text() + '</span>');
});
//搜索结果不再跳转页面
$('#results').on('click', 'a', function () {
if (HISTORY_STATE) {
var $this = $(this);
var href = $this.attr('href');
if (typeof href != 'undefined' && href != '') {
var path = href.split('file=')[1];
search.displayBox('off'); //关闭搜索面板
changeNav(path);
changePage(path);
$this.trigger('navchange');
}
return false;
}
});
//设置导航筛选初始值
var filterVal = storage.getStates('navFilterKey');
if (typeof filterVal != 'undefined' && filterVal != '') {
$filter.val(filterVal).trigger('input2');
}
//回调
callback && callback(pathList);
}, 'text');
};
$.get('library/$navigation.md?t=' + Date.now(), fillNav, 'text').fail(function () {
if (typeof AWPageMounts != 'undefined') {
fillNav(AWPageMounts['nav'].content);
}
});
};
//读取页面挂载数据文档部分
var loadPageMounts = function () {
if (typeof AWPageMounts == 'undefined') {
return;
}
//打开页面立即比较页面挂载数据时间与本地缓存更新时间
// 因为首页总是更新的,如果首页页面挂载数据时间大于本地缓存时间,则挂载数据一定已经经过重建
var homePath = AWPageMounts['home'].name.replace(/\.md$/, '');
var homeStorageTime = storage.readTime(homePath);
//如果页面挂载数据经过了重建,开始更新,读取所有挂载数据插入到本地缓存,完成后删除释放资源占用
if (AWPageMounts['home'].timestamp > homeStorageTime) {
//首页
storage.saveDoc(homePath, AWPageMounts['home'].content);
delete AWPageMounts['home'];
//其他文档
for (var lv1 in AWPageMounts) {
if (!AWPageMounts.hasOwnProperty(lv1)) {
continue;
}
if (!/^m\d+/.test(lv1)) {
continue;
}
for (var i = 0, page; page = AWPageMounts[lv1][i]; i++) {
storage.saveDoc(page.path.replace(/\.md$/, ''), page.content);
}
delete AWPageMounts[lv1];
}
storage.saveRebuild();
}
//否则直接删除页面挂载数据的文档部分,释放内存资源占用
else {
delete AWPageMounts['home'];
for (var name in AWPageMounts) {
if (AWPageMounts.hasOwnProperty(name)) {
if (/^m\d+/.test(name)) {
delete AWPageMounts[name];
}
}
}
}
};
//根据hash改变滚动位置
var changeScrollByHash = function () {
var hash = location.hash.split('#')[1];
//当不存在hash
if (!hash || hash.length == '') {
//检测是否在顶部,不在顶部滚动至顶部
if ($mainInner.scrollTop() != 0) {
$mainInner.scrollTop(0);
}
return;
}
//获取hash指向的元素
var $hash = $('.anchor[name="' + hash + '"]');
if ($hash.length == 0) {
return
}
//滚动至元素
$mainInner.scrollTop($hash.position().top + $mainInner.scrollTop() - 10);
};
/*
启动应用
*/
//解析地址参数
var path = tools.getURLParameter('file');
if (!path) {
path = '首页';
} else {
path = decodeURI(path);
var curPath = tools.getURLParameter('file');
curPath = !curPath ? '首页' : decodeURI(curPath);
curPath = curPath.replace(/%26/g, '&');
//加载导航
loadNav(function (list) {
//核对本地存储
storage.checkLibChange(list);
//读取页面挂载数据文档部分
loadPageMounts();
//首次打开改变导航
changeNav(curPath);
//首次打开改变页面
changePage(curPath, true, changeScrollByHash);
});
//history api 浏览器前进后退操作响应
if (HISTORY_STATE) {
$win.on('popstate', function (e) {
var path;
//当有状态记录时,直接跳转
if (e.originalEvent.state) {
path = e.originalEvent.state.path;
path = path.replace(/%26/g, '&');
//改变导航
changeNav(path);
//改变页面
changePage(path, true, changeScrollByHash);
}
//当没有状态记录时
else {
path = tools.getURLParameter('file');
path = !path ? '首页' : decodeURI(path);
path = path.replace(/%26/g, '&');
//判断 url 路径是否和当前一样,不一样才跳转
if (path != curPath) {
//改变导航
changeNav(path);
//改变页面
changePage(path, true, changeScrollByHash);
}
//相同时不跳转,根据 hash 变化改变位置
else {
changeScrollByHash();
}
}
});
}
/*
回调中转
*/
//重建缓存
search.onNeedRebuildStorage = function (callback) {
storage.clearLibraries();
@ -330,29 +674,6 @@ $(function () {
}
};
//页面基本
pageBase();
//加载导航
loadNav(function (list) {
//核对本地存储
storage.checkLibChange(list);
//首次打开改变导航
changeNav(path);
//首次打开改变页面
changePage(path, true);
});
//history api 浏览器前进后退操作响应
if (HISTORY_STATE) {
$(window).on('popstate', function (e) {
var path = e.originalEvent.state.path;
//改变导航
changeNav(path);
//改变页面
changePage(path, true);
});
}
});

288
files/amWiki.scrollbar.js Normal file
View File

@ -0,0 +1,288 @@
/**
* amWiki Web端 - 滚动条模块
* @author Tevin
*/
;
(function (win, $) {
'use strict';
//定时检测内容高度作为补充
var list = [];
setInterval(function () {
for (var i = 0, item; item = list[i]; i++) {
item.checkHeightWidth();
}
}, 500);
/**
* 页面滚动
* @param {Element} _this
* @constructor
*/
var Scroller = function (_this) {
this.$e = {
container: $(_this), //主容器
inner: null, //正文内容
barY: null, //垂直滚动条外层
boxY: null, //垂直滚动条
sliderY: null, //垂直滑块
barX: null, //水平滚动条外层
boxX: null, //水平滚动条
sliderX: null //水平滑块
};
this._data = {
xRolling: this.$e.container.attr('data-x-rolling') == 'true',
maxTop: 0, //最大top值
barH: 0, //滚动条高度
sliderH: 0, //滑块高度
contentH: 0, //内容高度
containerH: 0, //容器高度
contentHLast: 0, //上次内容高度
barW: 0, //滚动条宽度
sliderW: 0, //滑块宽度
contentW: 0, //内容宽度
containerW: 0, //容器宽度
contentWLast: 0 //上次内容宽度
};
this._init();
};
/**
* 初始化
* @private
*/
Scroller.prototype._init = function () {
var that = this;
var barHtml = '<div class="scrollbar scrollbar-y" onselectstart="return false"><div><i></i></div></div>';
barHtml += this._data.xRolling ?
'<div class="scrollbar scrollbar-x" onselectstart="return false"><div><i></i></div></div>' : '';
this.$e.container.append(barHtml);
//获取元素
this.$e.barY = this.$e.container.find('.scrollbar-y');
this.$e.boxY = this.$e.barY.children('div');
this.$e.sliderY = this.$e.barY.find('i');
if (this._data.xRolling) {
this.$e.barX = this.$e.container.find('.scrollbar-x');
this.$e.boxX = this.$e.barX.children('div');
this.$e.sliderX = this.$e.barX.find('i');
}
this.$e.inner = this.$e.container.children('.scroller-inner');
//绑定事件
this._onWinResize();
$(win).on('resize', function () {
that._onWinResize();
that.checkHeightWidth();
});
this.$e.container.on('scrollbar', function () {
that.checkHeightWidth();
});
this.$e.inner.on({
'click': function () {
that.checkHeightWidth();
},
'scroll': function () {
that._reScroll();
}
});
this.checkHeightWidth();
this._bindAction();
};
/**
* 滑块操作
* @private
*/
Scroller.prototype._bindAction = function () {
var that = this;
var onDrag = false;
var _y = 0,
top = 0,
_x = 0,
left = 0;
this.$e.body = $('body');
this.$e.boxY.on({
'mousedown': function (e) {
onDrag = 'y';
that.$e.body.attr('onselectstart', 'return false');
that.$e.barY.addClass('active');
_y = e.pageY;
top = parseFloat(that.$e.sliderY.css('top'));
if (that.$e.barY.parents('#main').length > 0) {
that.$e.body.children('aside').hide();
}
}
});
if (this._data.xRolling) {
this.$e.boxX.on({
'mousedown': function (e) {
onDrag = 'x';
that.$e.body.attr('onselectstart', 'return false');
that.$e.barX.addClass('active');
_x = e.pageX;
left = parseFloat(that.$e.sliderX.css('left'));
}
});
}
$(win.document).on({
'mousemove': function (e) {
if (onDrag == 'y') {
that.scrollYTo(top + e.pageY - _y);
} else if (onDrag == 'x') {
that.scrollXTo(left + e.pageX - _x);
}
},
'mouseup': function () {
that.$e.body.removeAttr('onselectstart', 'return false')
.children('aside').show();
if (onDrag == 'y') {
that.$e.barY.removeClass('active');
} else if (onDrag == 'x') {
that.$e.barX.removeClass('active');
}
onDrag = false;
}
});
};
/**
* 尺寸缩放
* @private
*/
Scroller.prototype._onWinResize = function () {
if (isMobi()) {
this.$e.inner.removeAttr('style').removeClass('on');
} else {
this.$e.inner.css({
width: this.$e.container.width() + 30,
paddingRight: 13,
paddingBottom: 16
}).addClass('on');
}
};
/**
* 重设滑块大小
* @private
*/
Scroller.prototype._resize = function () {
var that = this;
this._data.containerH = this.$e.inner.height();
//当内容高度小于等于容器时,不显示滚动条
if (this._data.contentH <= this._data.containerH) {
this._data.contentH = this._data.containerH;
this.$e.barY.addClass('off');
} else {
this.$e.barY.removeClass('off');
}
//设置滑块大小
this._data.barH = this.$e.boxY.height();
this._data.sliderH = this._data.containerH / that._data.contentH * this._data.barH;
this.$e.sliderY.height(this._data.sliderH);
//计算最大高度
this._data.maxTop = (this._data.barH - this._data.sliderH) / this._data.barH * this._data.contentH + 1;
//水平方向
if (this._data.xRolling) {
this._data.containerW = this.$e.inner.width();
//当内容宽度不超过容器时,不显示滚动条
if (this._data.contentW <= this._data.containerW) {
this._data.contentW = this._data.containerW;
this.$e.barX.addClass('off');
} else {
this.$e.barX.removeClass('off');
}
//设置滑块大小
this._data.barW = this.$e.boxX.width();
this._data.sliderW = this._data.containerW / that._data.contentW * this._data.barW;
this.$e.sliderX.width(this._data.sliderW);
}
};
/**
* 重设滑块顶部距离
* @private
*/
Scroller.prototype._reScroll = function () {
var that = this;
var top1 = this.$e.inner.scrollTop();
var barTop = top1 / this._data.contentH * this._data.barH;
//限制滑动边界 (由事件触发的滚动不会向上过界)
if (barTop + this._data.sliderH >= this._data.barH) {
barTop = this._data.barH - this._data.sliderH;
this.$e.sliderY.css('top', barTop);
this.$e.inner.scrollTop(this._data.maxTop);
} else {
this.$e.sliderY.css('top', barTop);
}
//水平方向
if (this._data.xRolling) {
var barLeft = this.$e.inner.scrollLeft() / this._data.contentW * this._data.barW;
//水平方向没有鼠标滚动事件
this.$e.sliderX.css('left', barLeft);
}
};
/**
* 垂直滚动到某位置
* @param {Number} num
* @public
*/
Scroller.prototype.scrollYTo = function (num) {
var barTop = num;
barTop = barTop < 0 ? 0 : barTop;
barTop = barTop + this._data.sliderH > this._data.barH ? this._data.barH - this._data.sliderH : barTop;
this.$e.sliderY.css('top', barTop);
this.$e.inner.scrollTop(barTop / this._data.barH * this._data.contentH);
};
/**
* 水平滚动到某位置
* @param {Number} num
* @public
*/
Scroller.prototype.scrollXTo = function (num) {
var barLeft = num;
barLeft = barLeft < 0 ? 0 : barLeft;
barLeft = barLeft + this._data.sliderW > this._data.barW ? this._data.barW - this._data.sliderW : barLeft;
this.$e.sliderX.css('left', barLeft);
this.$e.inner.scrollLeft(barLeft / this._data.barW * this._data.contentW);
};
/**
* 检查高度宽度
* @public
*/
Scroller.prototype.checkHeightWidth = function () {
var that = this;
this._data.contentH = 0;
this._data.contentW = 0;
this.$e.inner.children('.scroller-content').each(function () {
that._data.contentH += $(this).outerHeight();
that._data.contentW += $(this).outerWidth();
});
//修正 webkit 中滚动条本身占位
if (/webkit/.test(navigator.userAgent.toLowerCase())) {
that._data.contentH += 15;
}
//如果高度未改变不进行操作
if (this._data.contentH == this._data.contentHLast && this._data.contentW == this._data.contentWLast) {
return;
} else {
this._data.contentHLast = this._data.contentH;
this._data.contentWLast = this._data.contentW;
}
this._resize();
this._reScroll();
};
//注册到 jQuery
$.extend($.fn, {
scrollbar: function () {
return this.each(function () {
list.push(new Scroller(this));
});
}
});
})(window, jQuery);

View File

@ -15,6 +15,10 @@
.search-update .text i{font-style:normal;}
.search-update input{position:relative;z-index:10;float:right;margin-top:6px;padding:4px 12px;}
.search-update input[disabled]{padding:5px 13px;color:#ccc;background:#f2f2f2;border:#aaa 1px solid;border-radius:1px;}
.search-update.off .text:after,.search-update.off .text span,.search-update.off .text i,.search-update.off input{display:none;}
.search-update.off .text b {margin-right:0;}
.search-update.off .text{padding-right:5px;}
.search-box h2{height:65px;color:#666;font-size:24px;font-weight:normal;}
/* ---------- search-input ---------- */
@ -25,7 +29,7 @@
.search-input input[type="button"][disabled]{color:#ccc;background:#f2f2f2;border:#aaa 1px solid;border-radius:1px;}
/* ---------- search-results ---------- */
.search-results{background:#eee;overflow-y:scroll;}
.search-results{overflow-y:scroll;background:#eee;border-top:#eee 1px solid;border-bottom:#eee 1px solid;}
.search-result-message{display:none;padding-top:50px;text-align:center;color:#777;font-size:22px;line-height:35px;}
.search-list{list-style:none;padding:18px 16px 5px;}
.search-item{margin-bottom:14px;background:#fff;border:#e6e6e6 1px solid;}
@ -56,3 +60,6 @@
.search-input{padding-right:90px;}
.search-input input[type="button"]{width:86px;}
}
@media screen and (max-width:720px){
.search-box{height:auto;min-height:100%;}
}

View File

@ -1,5 +1,5 @@
/**
* @desc amWiki Web端·全库搜索
* amWiki Web端 - 全库搜索模块
* @author Tevin
*/
@ -9,27 +9,29 @@
'use strict';
/**
* @class 创建全局搜索
* 全局搜索
* @param {Storage} _storage
* @constructor
*/
var Search = function (_storage) {
this._storage = _storage;
this.elm = {
this.$e = {
//显示搜索面板按钮
$searchShow: $('#searchShow'),
searchShow: $('#searchShow'),
//更新全部缓存按钮
$searchUpdate: this._storage.elm.$searchUpdate,
searchUpdate: this._storage.$e.searchUpdate,
//搜索面板
$searchBox: $('#searchBox'),
searchBox: $('#searchBox'),
//搜索结果列表
$results: $('#results'),
results: $('#results'),
//搜索结果信息提示
$resultMsg: $('#resultMsg'),
resultMsg: $('#resultMsg'),
//搜素结果显示更多
$resultMore: $('#resultMore'),
resultMore: $('#resultMore'),
//搜索按钮
$search: $('#search'),
search: $('#search'),
//搜索文本
$searchText: $('#searchText')
searchText: $('#searchText')
};
this._data = {
//搜素结果
@ -46,43 +48,52 @@
this.onNeedRebuildStorage = null;
};
//初始化
/**
* 绑定用户操作
* @private
*/
Search.prototype._bindCtrl = function () {
var that = this;
//展开折叠搜索面板
this.elm.$searchShow.on('click', function () {
if (that.elm.$searchBox.hasClass('on')) {
this.$e.searchShow.on('click', function () {
if (that.$e.searchBox.hasClass('on')) {
that.displayBox('off');
that.$e.searchShow.trigger('searchoff');
} else {
that.displayBox('on', function () {
resetResHeight();
});
that.$e.searchShow.trigger('searchon');
}
});
//设置结果区域高度
var resetResHeight = function () {
var hOut = that.elm.$searchBox.height();
var dt = that.elm.$results.offset().top - that.elm.$searchUpdate.offset().top;
that.elm.$results.height(hOut - dt);
var hOut = that.$e.searchBox.height();
var dt = that.$e.results.offset().top - that.$e.searchUpdate.offset().top;
that.$e.results.height(hOut - dt);
};
$(win).on('resize', function () {
if (that.elm.$searchBox.hasClass('on')) {
if (that.$e.searchBox.hasClass('on')) {
resetResHeight();
}
});
//当本地浏览且存在页面挂载数据时,隐藏重建缓存按钮
if (location.protocol == 'file:' && typeof AWPageMounts != 'undefined') {
this.$e.searchUpdate.parent().addClass('off');
}
//重建缓存
this.elm.$searchUpdate.on('click', function () {
this.$e.searchUpdate.on('click', function () {
//开启重建缓存时,如果存在搜索子进程,则干掉子进程
if (that._worker) {
that._worker.terminate();
that._worker = null;
that.elm.$resultMsg.hide();
that.$e.resultMsg.hide();
}
that.elm.$search.prop('disabled', true);
that.elm.$searchUpdate.prop('disabled', true);
that.$e.search.prop('disabled', true);
that.$e.searchUpdate.prop('disabled', true);
that.onNeedRebuildStorage(function () {
that.elm.$search.prop('disabled', false);
that.elm.$searchUpdate.val('请勿频繁使用');
that.$e.search.prop('disabled', false);
that.$e.searchUpdate.val('请勿频繁使用');
});
});
//更新全部缓存按钮使用的时间限制:一小时内不允许重复使用
@ -90,28 +101,33 @@
if (lastBuild) {
var lave = Date.now() - lastBuild;
if (lave < 60 * 60 * 1000) {
this.elm.$searchUpdate.prop('disabled', true).val('请勿频繁使用');
this.$e.searchUpdate.prop('disabled', true).val('请勿频繁使用');
}
}
//点击搜索
this.elm.$search.on('click', function () {
this.$e.search.on('click', function () {
that._search();
});
this.elm.$searchText.on('keyup', function (e) {
this.$e.searchText.on('keyup', function (e) {
if (e.keyCode == 13) {
that._search();
}
});
//结果翻页
this.elm.$resultMore.on('click', function () {
this.$e.resultMore.on('click', function () {
that._nextResultPage();
});
};
//显示隐藏搜索面板
/**
* 显示隐藏搜索面板
* @param {String} type - on 显示 off 隐藏
* @param {Function} callback
* @public
*/
Search.prototype.displayBox = function (type, callback) {
var that = this;
var $box = this.elm.$searchBox;
var $box = this.$e.searchBox;
if (type == 'on' && !$box.hasClass('on')) {
$box
.addClass('on')
@ -139,66 +155,104 @@
}
};
//启动搜素
/**
* 启动搜素
* @private
*/
Search.prototype._search = function () {
var that = this;
if (this.elm.$searchText.val() == '') {
this.elm.$searchText.focus();
if (this.$e.searchText.val() == '') {
this.$e.searchText.focus();
return;
}
var words = this.elm.$searchText.val();
var words = this.$e.searchText.val();
this.$e.resultMsg.show().text('创建搜索中...');
if (typeof win.Worker !== "undefined") {
//开启一次新搜索时,如果存在搜索子进程,则干掉子进程
if (this._worker) {
this._worker.terminate();
this._worker = null;
this.elm.$resultMsg.hide();
this.$e.resultMsg.hide();
}
try {
//创建搜素子进程搜素
this._worker = new win.Worker('amWiki/js/amWiki.search.worker.js');
this._searchByWorker(words);
} catch (e) {
//在当前环境搜索
this._searchByPresent(words);
}
this.elm.$resultMsg.show().text('创建搜索中...');
//创建子进程
this._worker = new win.Worker('amWiki/js/amWiki.search.worker.js');
//收到子进程搜素消息
this._worker.onmessage = function (event) {
var data = event.data;
//加载成功后发送文档数据
if (data.type == 'loaded') {
that._worker.postMessage({type: 'docs', docs: that._storage.getAllDocs()});
}
//文档预处理完成后开始搜索
else if (data.type == 'ready') {
that.elm.$resultMsg.show().html('正在搜索,请稍后...');
that._worker.postMessage({type: 'search', words: words});
}
//搜索结果排行
else if (data.type == 'result') {
that._data.result = data.result;
that._showResultList();
that._worker.terminate();
that._worker = null;
}
};
//子进程出错
this._worker.onerror = function (e) {
console.error(e);
that._worker.terminate();
that._worker = null;
};
} else {
this.elm.$resultMsg.show().text('Sorry您的浏览器不支持搜索');
this.elm.$search.prop('disabled', true);
//在当前环境搜索
this._searchByPresent(words);
}
};
//显示结果列表
/**
* 搜索子进程通讯
* @param {String} words
* @private
*/
Search.prototype._searchByWorker = function (words) {
var that = this;
//收到子进程搜素消息
this._worker.onmessage = function (event) {
var data = event.data;
//加载成功后发送文档数据
if (data.type == 'searcher:loaded') {
that._worker.postMessage({type: 'searcher:docs', docs: that._storage.getAllDocs()});
}
//文档预处理完成后开始搜索
else if (data.type == 'searcher:ready') {
that.$e.resultMsg.show().html('正在搜索,请稍后...');
that._worker.postMessage({type: 'searcher:search', words: words});
}
//搜索结果排行
else if (data.type == 'searcher:result') {
that._data.result = data.result;
that._showResultList();
that._worker.terminate();
that._worker = null;
}
};
//子进程出错
this._worker.onerror = function (e) {
console.error(e);
this.$e.resultMsg.show().text('Sorry出错了<br/>' + e.msg);
that._worker.terminate();
that._worker = null;
};
};
/**
* 在当前环境搜索
* @param {String} words
* @private
*/
Search.prototype._searchByPresent = function (words) {
var searcher = new AWSearcher();
searcher.initDocs(this._storage.getAllDocs());
this.$e.resultMsg.show().html('正在搜索,请稍后...');
searcher.matchWords(words);
this._data.result = searcher.getResult();
this._showResultList();
};
/**
* 显示结果列表
* @private
*/
Search.prototype._showResultList = function () {
this.elm.$results.children('ul').children().remove();
this.elm.$resultMsg.hide();
this.$e.results.children('ul').children().remove();
this.$e.resultMsg.hide();
this._data.pagination = 0;
this._nextResultPage();
};
//显示结果列表下一页
/**
* 显示结果列表下一页
* @private
*/
Search.prototype._nextResultPage = function () {
var html = '';
var count = 0;
@ -209,17 +263,23 @@
break;
}
}
this.elm.$results.children('ul').append(html);
this.$e.results.children('ul').append(html);
this._data.pagination++;
//如果还有结果没显示完,显示显示更多按钮
if (this._data.pagination * this._data.pageSize >= this._data.result.length) {
this.elm.$resultMore.hide();
this.$e.resultMore.hide();
} else {
this.elm.$resultMore.show();
this.$e.resultMore.show();
}
};
//渲染单条模板
/**
* 渲染单条模板
* @param {String} template
* @param {Object} data
* @returns {String}
* @private
*/
Search.prototype._renderRankItem = function (template, data) {
var tmpl = template;
data.time = win.tools.formatTime(data.timestamp);

View File

@ -1,5 +1,5 @@
/**
* @desc amWiki Web端·搜索子进程
* amWiki Web端 - 搜索计算子进程模块
* @author Tevin
*/
@ -8,9 +8,15 @@
'use strict';
//计算器
var searcher = (function () {
//通过 jQuery 检查判断是否处于子进程工作
var atWorker = typeof self.jQuery == 'undefined';
(function () {
/**
* 搜索计算器
* @constructor
*/
var Searcher = function () {
//文档存储
this._documents = null;
@ -29,7 +35,7 @@
//初始文档
Searcher.prototype.initDocs = function (docs) {
this._documents = docs;
this._documents = atWorker ? docs : JSON.parse(JSON.stringify(docs));
for (var id in this._documents) {
if (this._documents.hasOwnProperty(id)) {
this._preDoc(this._documents[id]);
@ -48,10 +54,10 @@
})
//分离测试文档请求地址
.replace(/([^#]#{3} *请求地址[\n\r]{1,4})([-\w:\/\.]+?)[\n\r]{1,}(#{3} *请求类型[\s\S]+?#{3} *请求参数)/,
function (match, s1, s2, s3) {
doc.api = s2;
return s1 + s3;
})
function (match, s1, s2, s3) {
doc.api = s2;
return s1 + s3;
})
//清除 Markdown 标题标记
.replace(/#{1,6}(.*?)#{0,6}\s*[\r\n]/g, '$1')
//清除 Markdown 强调斜体删除线标记
@ -108,34 +114,46 @@
//匹配搜索词与得分计算
Searcher.prototype.matchWords = function (words) {
var wordsReg = new RegExp(words, 'gi');
for (var id in this._documents) {
if (this._documents.hasOwnProperty(id)) {
//标题命中
if (this._documents[id].title && this._documents[id].title.indexOf(words) >= 0) {
var title = this._documents[id].title.replace(words, '<mark>' + words + '</mark>');
this._addPorcessing(id, 'title', title);
this._addPorcessing(id, 'score', this._data.titleScore);
if (this._documents[id].title) {
var titleMatch = this._documents[id].title.match(wordsReg);
if (titleMatch && titleMatch.length > 0) {
var title = this._documents[id].title.replace(wordsReg, function (match) {
return '<mark>' + match + '</mark>';
});
this._addPorcessing(id, 'title', title);
this._addPorcessing(id, 'score', this._data.titleScore);
}
}
//接口地址命中
if (this._documents[id].api && this._documents[id].api.indexOf(words) >= 0) {
var api = '<p class="p1"><em>接口</em>' + this._documents[id].api.replace(words, '<mark>' + words + '</mark>') + '</p>';
this._addPorcessing(id, 'api', api);
this._addPorcessing(id, 'score', this._data.apiScore);
if (this._documents[id].api) {
var apiMatch = this._documents[id].api.match(wordsReg);
if (apiMatch && apiMatch.length > 0) {
var api = '<p class="p1"><em>接口</em>' +
this._documents[id].api.replace(wordsReg, function (match) {
return '<mark>' + match + '</mark>';
}) + '</p>';
this._addPorcessing(id, 'api', api);
this._addPorcessing(id, 'score', this._data.apiScore);
}
}
//内容命中
if (this._documents[id].content.indexOf(words) >= 0) {
var matches = this._documents[id].content.match(new RegExp('.{0,15}' + words + '.{0,30}', 'g'));
if (matches) {
var content = '<p>';
for (var i = 0, item; item = matches[i]; i++) {
if (i < 2) {
content += item.replace(words, '<mark>' + words + '</mark>') + '... ';
}
var contentMatch = this._documents[id].content.match(new RegExp('.{0,15}' + words + '.{0,30}', 'gi'));
if (contentMatch) {
var content = '<p>';
for (var i = 0, item; item = contentMatch[i]; i++) {
if (i < 2) {
content += item.replace(wordsReg, function (match) {
return '<mark>' + match + '</mark>';
}) + '... ';
}
content += '</p>';
this._addPorcessing(id, 'content', content);
this._addPorcessing(id, 'score', matches.length * this._data.textScore);
}
content += '</p>';
this._addPorcessing(id, 'content', content);
this._addPorcessing(id, 'score', contentMatch.length * this._data.textScore);
}
}
}
@ -176,21 +194,27 @@
return this._sortByScore();
};
return new Searcher();
//作为子进程加载时,仅子进程内有效
//作为全局加载时,全局有效
return this.AWSearcher = Searcher;
})();
}).call(self);
self.onmessage = function (event) {
var data = event.data;
if (data.type == 'docs') {
searcher.initDocs(data.docs);
self.postMessage({type: 'ready'});
} else if (data.type == 'search') {
searcher.matchWords(data.words);
self.postMessage({type: 'result', result: searcher.getResult()});
}
};
self.postMessage({type: 'loaded'});
//作为子进程工作时,通过 message 通讯工作
if (atWorker) {
//计算器
var searcher = new self.AWSearcher();
self.onmessage = function (event) {
var data = event.data;
if (data.type == 'searcher:docs') {
searcher.initDocs(data.docs);
self.postMessage({type: 'searcher:ready'});
} else if (data.type == 'searcher:search') {
searcher.matchWords(data.words);
self.postMessage({type: 'searcher:result', result: searcher.getResult()});
}
};
self.postMessage({type: 'searcher:loaded'});
}
})(self);

View File

@ -1,5 +1,5 @@
/**
* @desc amWiki Web端·本地缓存模块
* amWiki Web端 - 浏览器数据缓存模块
* @author Tevin
*/
@ -11,28 +11,35 @@
var tools = win.tools;
var wikiPath = tools.simString(win.location.pathname.replace('/', '').replace(/\//g, '_')).toUpperCase();
var LOCAL_STORAGE_NAME = 'AMWikiDataBase@' + wikiPath; //本地数据localStorage键名
var LOCAL_STATES = 'AMWikiStates@' + wikiPath; //本地状态集键名
/**
* @class 创建一个本地存储管理对象
* 本地存储管理
* @constructor
*/
var Storage = function () {
this._db = null; //内存中的文库缓存
this.elm = {
$win: $(win),
this._states = null; //内存中的状态集
this.$e = {
win: $(win),
//更新全部缓存按钮
$searchUpdate: $('#searchUpdate'),
searchUpdate: $('#searchUpdate'),
//缓存状态
$cacheState: $('#cacheState'),
cacheState: $('#cacheState'),
//文档总数
$cacheDocTotal: $('#cacheDocTotal'),
cacheDocTotal: $('#cacheDocTotal'),
//上次全部缓存更新时间
$cacheLasttime: $('#cacheLasttime')
cacheLasttime: $('#cacheLasttime')
};
this._bridgeLocalStorage('read');
this._bindCtrl();
};
//存取本地存储
/**
* 存取本地存储
* @param {String} type - read / save
* @private
*/
Storage.prototype._bridgeLocalStorage = function (type) {
if (type == 'read') {
var defaultStr = '{"documents":{},"lastBuild":0}';
@ -47,15 +54,24 @@
}
};
//绑定操作
/**
* 绑定操作
* @private
*/
Storage.prototype._bindCtrl = function () {
var that = this;
this.elm.$win.on('beforeunload', function () {
this.$e.win.on('beforeunload', function () {
that._bridgeLocalStorage('save');
});
};
//更新一篇文档,如果相同则不操作(对应内容不用重新渲染)
/**
* 更新一篇文档如果相同则不操作(对应内容不用重新渲染)
* @param {String} uri
* @param {String} content
* @returns {Boolean}
* @public
*/
Storage.prototype.update = function (uri, content) {
var id = tools.simString(uri, 'short');
if (this._db.documents[id]) {
@ -72,10 +88,11 @@
};
/**
* @desc 保存一篇文档
* @param uri {string} - 文档资源地址
* @param content {string} - 文档内容
* @param [id] {string} - 已经编码的文档地址可选
* 保存一篇文档
* @param {String} uri - 文档资源地址
* @param {String} content - 文档内容
* @param {String} [id] - 已经编码的文档地址可选
* @public
*/
Storage.prototype.saveDoc = function (uri, content, id) {
this.saveDocToDB(uri, content, id);
@ -83,7 +100,13 @@
this._changeSummary('sateOnly');
};
//将文档存储到内存
/**
* 将文档存储到内存
* @param {String} uri
* @param {String} content
* @param {String} id
* @public
*/
Storage.prototype.saveDocToDB = function (uri, content, id) {
if (typeof uri != 'string' && uri == '') {
throw new Error('Error, uri must be a string!');
@ -101,9 +124,10 @@
};
/**
* @desc 读取一篇文档
* @param uri {string} - 文档资源地址
* @returns {string} - 文档内容
* 读取一篇文档
* @param {String} uri - 文档资源地址
* @returns {String} 文档内容
* @public
*/
Storage.prototype.read = function (uri) {
var id = tools.simString(uri, 'short');
@ -115,8 +139,24 @@
};
/**
* @desc 删除一篇文档
* @param uri {string} - 文档资源地址
* 读取一篇文档的时间
* @param {String} uri - 文档资源地址
* @returns {String} 文档内容
* @public
*/
Storage.prototype.readTime = function (uri) {
var id = tools.simString(uri, 'short');
if (this._db.documents[id]) {
return this._db.documents[id].timestamp;
} else {
return 0;
}
};
/**
* 删除一篇文档
* @param {String} uri - 文档资源地址
* @public
*/
Storage.prototype.remove = function (uri) {
var id = tools.simString(uri, 'short');
@ -125,15 +165,20 @@
this._changeSummary('sateOnly');
};
//增涨文档打开数记录
/**
* 增涨文档打开数记录
* @param {String} uri
* @public
*/
Storage.prototype.increaseOpenedCount = function (uri) {
var id = tools.simString(uri, 'short');
//TODO: 待续...打开次数将一定程度影响排行
};
/**
* @desc 校对列表清除失效文档
* @param list {Array} - 由导航树偏平化生成的文档列表
* 校对列表清除失效文档
* @param {Array} list - 由导航树偏平化生成的文档列表
* @public
*/
Storage.prototype.checkLibChange = function (list) {
this._indexing = list;
@ -150,7 +195,12 @@
this._changeSummary();
};
//更新缓存摘要(位于搜素面板)
/**
* 更新缓存摘要位于搜素面板
* @param {String} stateOnly - 是否为只读 stateOnly / ...
* @param {String} prepare - 是否为预先 prepare / ...
* @private
*/
Storage.prototype._changeSummary = function (stateOnly, prepare) {
var libraryiesLong = 0;
for (var p in this._db.documents) {
@ -160,51 +210,100 @@
}
//如果是预先百分数减1
if (prepare == 'prepare') {
this.elm.$cacheState.text(parseInt(libraryiesLong / this._indexing.length * 100 - 1) + '%');
this.$e.cacheState.text(parseInt(libraryiesLong / this._indexing.length * 100 - 1) + '%');
}
//非预先则正常
else {
this.elm.$cacheState.text(parseInt(libraryiesLong / this._indexing.length * 100) + '%');
this.$e.cacheState.text(parseInt(libraryiesLong / this._indexing.length * 100) + '%');
}
//如果不只是状态
if (stateOnly != 'stateOnly') {
this.elm.$cacheDocTotal.text(this._indexing.length);
this.$e.cacheDocTotal.text(this._indexing.length);
if (this._db.lastBuild) {
this.elm.$cacheLasttime.text(win.tools.formatTime(this._db.lastBuild));
this.$e.cacheLasttime.text(win.tools.formatTime(this._db.lastBuild));
} else {
this.elm.$cacheLasttime.text('0000-00-00 00:00:00');
this.$e.cacheLasttime.text('0000-00-00 00:00:00');
}
}
};
//清除内存中的库列表
/**
* 清除内存中的库列表
* @public
*/
Storage.prototype.clearLibraries = function () {
this._db.documents = {};
this._changeSummary('sateOnly');
};
//完成本次缓存重建
/**
* 完成本次缓存重建
* @public
*/
Storage.prototype.saveRebuild = function () {
this._db.lastBuild = Date.now();
this._bridgeLocalStorage('save');
this._changeSummary();
};
//返回导航列表
/**
* 返回导航列表
* @returns {Array}
* @public
*/
Storage.prototype.getIndexList = function () {
return this._indexing;
};
//获取当前缓存的所有文档
/**
* 获取当前缓存的所有文档
* @returns {{Object}}
* @public
*/
Storage.prototype.getAllDocs = function () {
return this._db.documents;
};
//获取缓存最后重建时间
/**
* 获取缓存最后重建时间
* @returns {Number}
* @public
*/
Storage.prototype.getLastBuildTs = function () {
return this._db.lastBuild;
};
/**
* 获取本地存储中指定名称的值
* @param {String} name
* @returns {*}
* @public
*/
Storage.prototype.getStates = function (name) {
if (!this._states) {
this._states = JSON.parse(win.localStorage[LOCAL_STATES] || '{}');
}
return this._states[name];
};
/**
* 保持键值对到本地存储
* @param {String} name
* @param {*} value
* @public
*/
Storage.prototype.setStates = function (name, value) {
if (!this._states) {
this._states = JSON.parse(win.localStorage[LOCAL_STATES] || '{}');
}
if (typeof value == 'undefined') {
delete this._states[name];
} else {
this._states[name] = value;
}
win.localStorage[LOCAL_STATES] = JSON.stringify(this._states);
};
return win.AWStorage = Storage;
})(window);

View File

@ -4,9 +4,14 @@
.testing-show{display:none;position:absolute;top:0;right:0;padding:15px;cursor:pointer;}
.testing-show.show{display:block;}
.testing-show span{color:@colour-dark;}
.testing-show[disabled=disabled]{cursor:not-allowed;}
.testing-show[disabled=disabled] span{color:#999;}
.testing-show[disabled=disabled] i{display:none;}
.testing-show[disabled=disabled].on i{display:block;position:absolute;top:35px;right:8px;width:200px;padding:5px 7px;color:#fff;font-size:12px;line-height:18px;background:rgba(0,0,0,.75);border-radius:5px;}
.testing-show[disabled=disabled].on i:after{display:block;position:absolute;top:-4px;right:32px;width:0;height:0;border-top:8px solid rgba(0,0,0,.75);border-left:8px solid transparent;content:' ';transform:rotate(-45deg);}
/* ---------- testing ---------- */
.testing-box{display:none;position:absolute;top:0;right:0;width:100%;padding:45px;background-color:#fff;}
.testing-box{display:none;position:absolute;top:0;right:0;width:100%;padding:45px;min-height:100%;background-color:#fff;}
.testing-box h2{position:relative;min-width:320px;padding:2px 10px;margin:5px 0;color:#fff;background-color:@colour-light;}
.testing-box h2:after{position:absolute;top:0;right:-45px;width:45px;height:100%;content:" ";background-color:@colour-light;}
.testing-box h2 small{position:absolute;top:0;right:0;font-size:14px;line-height:38px;font-weight:normal;}
@ -61,6 +66,7 @@
@media screen and (max-width:720px){
.testing-show{position:fixed;top:3px;right:40px;z-index:11000;height:46px;padding:0 10px;line-height:47px;background:#fff;}
.testing-box{padding:25px;}
.testing-box h2:after{right:-25px;width:25px;}
.testing-send{padding:20px 10px 35px;}
.testing-send-url{width:70%;}
.testing-param-val{width:45%;}

View File

@ -1,7 +1,8 @@
/**
* @desc amWiki Web端·简单ajax测试模块
* amWiki Web端 - 简单 ajax 测试模块
* @author Tevin
* @summary 仅当页面存在请求地址请求类型请求参数三个h3标题时触发
*
* @notice 仅当页面存在请求地址请求类型请求参数三个h3标题时触发且参数列表表格顺序不能打乱
*/
(function (win, $) {
@ -9,23 +10,26 @@
'use strict';
/**
* @class 创建接口测试
* 建接口测试
* @constructor
*/
var Testing = function () {
//缓存元素
this.elm = {
$win: $(win),
this.$e = {
win: $(win),
//测试面板
$testingBox: $('#testingBox'),
testingBox: $('#testingBox'),
//md文档渲染处
$view: $('#view'),
view: $('#view'),
//上一篇下一篇切换
sibling: $('#mainSibling'),
//面板显示隐藏按钮
$testingShow: null,
testingShow: null,
//参数列表的容器
$testingParam: $('#testingParam')
testingParam: $('#testingParam')
};
//缓存数据
this.data = {
this._data = {
//全局参数列队
globalParams: [],
//全局参数是否生效
@ -34,7 +38,7 @@
paramTemplate: $('#template\\:formList').text()
};
//请求数据
this.request = {
this._request = {
//请求地址
url: '',
//请求类型
@ -44,51 +48,52 @@
//全局参数
paramGlobal: []
};
this.useGlobalParam();
this.bindPanelCtrl();
this.bindAjaxSend();
this._useGlobalParam();
this._bindPanelCtrl();
this._bindAjaxSend();
};
/**
* @desc 抓取请求内容抓取成功才显示按钮
* 抓取请求内容抓取成功才显示按钮
* @public
*/
Testing.prototype.crawlContent = function () {
var that = this;
var testingReqState = [false, false, false];
this.elm.$testingShow.removeClass('show');
this.elm.$view.find('h3').each(function () {
this.$e.testingShow.removeClass('show');
this.$e.view.find('h3').each(function () {
var $this = $(this);
var name = $.trim($this.text());
//抓取请求地址
if (name == '请求地址' && !testingReqState[0]) {
that.request.url = $.trim($this.next().text());
if (that.request.url.indexOf('http') < 0) {
if (that.request.url.indexOf('/') == 0) {
that.request.url = 'http://' + location.host + that.request.url;
that._request.url = $.trim($this.next().text());
if (that._request.url.indexOf('http') < 0) {
if (that._request.url.indexOf('/') == 0) {
that._request.url = 'http://' + location.host + that._request.url;
} else {
that.request.url = 'http://' + location.host + '/' + that.request.url;
that._request.url = 'http://' + location.host + '/' + that._request.url;
}
}
testingReqState[0] = true;
}
//抓取请求类型
else if (name == '请求类型' && !testingReqState[1]) {
that.request.method = $.trim($this.next().text()).toUpperCase();
that._request.method = $.trim($this.next().text()).toUpperCase();
var methodState = false;
['GET', 'POST', 'PUT', 'DELETE'].forEach(function (value, index) {
if (that.request.method == value) {
if (that._request.method == value) {
methodState = true;
}
});
if (!methodState) {
that.request.method = 'POST';
that._request.method = 'POST';
}
testingReqState[1] = true;
}
//抓取请求参数
else if (name == '请求参数' && !testingReqState[2]) {
//清空参数列表
that.request.params.length = 0;
that._request.params.length = 0;
//不存在table直接无参数存在table时开始解析
if ($this.next('table').length > 0) {
$this.next('table').find('tbody').find('tr').each(function () {
@ -118,7 +123,7 @@
param.default = '';
}
}
that.request.params.push(param);
that._request.params.push(param);
}
});
}
@ -126,154 +131,180 @@
}
});
if (testingReqState[0] && testingReqState[1] && testingReqState[2]) {
this.initPanel();
this._initPanel();
} else {
this.offPanel();
this._offPanel();
}
testingReqState = [false, false, false];
};
//关闭测试面板
Testing.prototype.offPanel = function () {
this.elm.$testingShow.removeClass('show');
if (this.elm.$testingShow.hasClass('on')) {
/**
* 关闭测试面板
* @private
*/
Testing.prototype._offPanel = function () {
this.$e.testingShow.removeClass('show');
if (this.$e.testingShow.hasClass('on')) {
this.displayBox('off');
}
//清除抓取参数
this.request.url = '';
this.request.method = '';
this.request.params = [];
this._request.url = '';
this._request.method = '';
this._request.params = [];
//清空请求地址
$('#testingSendUrl').val('');
//还原请求类型
$('#testingSendType').find('option[value="POST"]').prop('selected', true);
//清空参数列表
this.elm.$testingParam.html('');
this.$e.testingParam.html('');
//重置iframe
$('#testingResponse')[0].contentWindow.location.reload();
};
//测试面板填充数据
Testing.prototype.initPanel = function () {
this.elm.$testingShow.addClass('show');
/**
* 测试面板填充数据
* @private
*/
Testing.prototype._initPanel = function () {
this.$e.testingShow.addClass('show');
//填充请求地址
$('#testingSendUrl').val(this.request.url);
$('#testingSendUrl').val(this._request.url);
//填充请求类型
$('#testingSendType').find('option[value="' + this.request.method + '"]').prop('selected', true);
$('#testingSendType').find('option[value="' + this._request.method + '"]').prop('selected', true);
//清空现有参数列表
this.elm.$testingParam.html('');
this.$e.testingParam.html('');
//填充参数列表
if (this.request.params.length > 0) {
if (this._request.params.length > 0) {
var paramsHTML = '';
for (var i = 0; i < this.request.params.length; i++) {
paramsHTML += this.data.paramTemplate
.replace('{{describe}}', this.request.params[i].describe)
.replace('{{keyName}}', this.request.params[i].keyName)
.replace('{{default}}', this.request.params[i].default)
.replace('{{valueType}}', this.request.params[i].valueType)
.replace('{{required}}', this.request.params[i].required);
for (var i = 0; i < this._request.params.length; i++) {
paramsHTML += this._data.paramTemplate
.replace('{{describe}}', this._request.params[i].describe)
.replace('{{keyName}}', this._request.params[i].keyName)
.replace('{{default}}', this._request.params[i].default)
.replace('{{valueType}}', this._request.params[i].valueType)
.replace('{{required}}', this._request.params[i].required);
}
this.elm.$testingParam.append(paramsHTML);
this.$e.testingParam.append(paramsHTML);
} else {
this.elm.$testingParam.append('<li>无</li>');
this.$e.testingParam.append('<li>无</li>');
}
};
//切换测试面板显示隐藏状态
/**
* 切换测试面板显示隐藏状态
* @param {String} type - on / off
* @public
*/
Testing.prototype.displayBox = function (type) {
var that = this;
if (type == 'off') {
this.elm.$testingShow.removeClass('on').find('span').text('测试接口');
this.elm.$testingBox.css({
'position': 'absolute',
'padding': this.elm.$win.width() > 720 ? 45 : 25
this.$e.testingShow.removeClass('on').find('span').text('测试接口');
this.$e.testingBox.css({
'position': 'absolute'
});
this.elm.$view.show();
this.elm.$testingBox.animate({
this.$e.view.show().addClass('scroller-content');
this.$e.sibling.addClass('scroller-content').addClass('on');
this.$e.testingBox.removeClass('scroller-content').stop().animate({
'width': '30%',
'opacity': 0
}, 200, 'swing', function () {
that.elm.$testingBox.removeAttr('style');
that.$e.testingBox.removeAttr('style');
});
} else if (type == 'on') {
this.elm.$testingShow.addClass('on').find('span').text('关闭测试');
this.elm.$testingBox
this.$e.testingShow.addClass('on').find('span').text('关闭测试');
this.$e.testingBox
.css({
'display': 'block',
'width': '0',
'min-height': this.elm.$view.height() + 90,
'opacity': 0
})
.stop()
.animate({
'width': '100%',
'opacity': 1
}, 300, 'swing', function () {
that.elm.$view.hide();
that.elm.$testingBox.css({
'position': 'relative',
'padding': 0
that.$e.view.hide().removeClass('scroller-content');
that.$e.sibling.removeClass('scroller-content').removeClass('on');
that.$e.testingBox.addClass('scroller-content').css({
'position': 'relative'
});
});
}
};
//测试面板普通操作
Testing.prototype.bindPanelCtrl = function () {
/**
* 测试面板普通操作
* @private
*/
Testing.prototype._bindPanelCtrl = function () {
var that = this;
//显示隐藏控制按钮
this.elm.$testingShow = $('<div class="testing-show">[<span>测试接口</span>]</div>');
$('#main').append(this.elm.$testingShow);
this.$e.testingShow = $('<div class="testing-show">[<span>测试接口</span>]</div>');
if (location.protocol == 'file:') {
this.$e.testingShow
.attr('disabled', 'disabled')
.append('<i>您当前为本地模式打开file:// 协议下不开放接口测试模块,请使用 http(s):// 网址打开</i>');
}
$('#main').append(this.$e.testingShow);
//显示隐藏测试面板
this.elm.$testingShow.on('click', function () {
if (that.elm.$testingShow.hasClass('on')) {
that.displayBox('off');
this.$e.testingShow.on('click', function () {
var $this = that.$e.testingShow;
if ($this.attr('disabled') == 'disabled') {
$this.toggleClass('on');
} else {
that.displayBox('on');
if (that.$e.testingShow.hasClass('on')) {
that.displayBox('off');
} else {
that.displayBox('on');
}
}
});
//填充请求地址
$('#testingSendUrl').on('change', function () {
that.request.url = $(this).val();
that._request.url = $(this).val();
});
//填充请求类型
$('#testingSendType').on('change', function () {
that.request.method = $(this).find("option:selected").val();
that._request.method = $(this).find("option:selected").val();
});
//清空所有普通参数的值
$('#testingBtnReset').on('click', function () {
that.elm.$testingParam.find('.testing-param-val').val('');
that.$e.testingParam.find('.testing-param-val').val('');
});
//新增一个参数
$('#testingBtnAdd').on('click', function () {
var pHTML = that.data.paramTemplate
var pHTML = that._data.paramTemplate
.replace('{{describe}}', '新增参数')
.replace('{{keyName}}', '')
.replace('{{default}}', '')
.replace('({{valueType}})', '')
.replace('{{required}}', '');
that.elm.$testingParam.append(pHTML);
that.$e.testingParam.append(pHTML);
});
};
//全局参数模块
Testing.prototype.useGlobalParam = function () {
/**
* 全局参数模块
* @private
*/
Testing.prototype._useGlobalParam = function () {
var that = this;
this.data.globalParams = JSON.parse(localStorage['amWikiGlobalParam'] || '[]'); //全局参数
this._data.globalParams = JSON.parse(localStorage['amWikiGlobalParam'] || '[]'); //全局参数
var gParamTmpl = $('#template\\:globalParam').text(); //全局参数模板
var $testingGlobalParam = $('#testingGlobalParam'); //全局参数显示容器
var $testingGlobal = $('#testingGlobal'); //全局参数弹窗
this.data.globalParamWorking = (localStorage['amWikiGParamWorking'] || 'on') == 'on'; //全局参数是否工作
this._data.globalParamWorking = (localStorage['amWikiGParamWorking'] || 'on') == 'on'; //全局参数是否工作
//显示弹窗
$('#testingBtnGParam').on('click', function () {
$testingGlobalParam.html('');
that.data.globalParams = JSON.parse(localStorage['amWikiGlobalParam'] || '[]');
if (that.data.globalParams.length == 0) {
that._data.globalParams = JSON.parse(localStorage['amWikiGlobalParam'] || '[]');
if (that._data.globalParams.length == 0) {
$testingGlobalParam.append('<li data-type="empty">无</li>');
} else {
for (var p = 0; p < that.data.globalParams.length; p++) {
$testingGlobalParam.append(gParamTmpl.replace('{{describe}}', that.data.globalParams[p].describe)
.replace('{{keyName}}', that.data.globalParams[p].keyName)
.replace('{{value}}', that.data.globalParams[p].value));
for (var p = 0; p < that._data.globalParams.length; p++) {
$testingGlobalParam.append(gParamTmpl.replace('{{describe}}', that._data.globalParams[p].describe)
.replace('{{keyName}}', that._data.globalParams[p].keyName)
.replace('{{value}}', that._data.globalParams[p].value));
}
}
$testingGlobal.show();
@ -294,18 +325,18 @@
}
//保存
else if ($elm.hasClass('save')) {
that.data.globalParams.length = 0;
that._data.globalParams.length = 0;
$testingGlobalParam.find('li').each(function (i, elment) {
var $inputs = $(this).find('input');
if ($inputs.eq(1).val()) {
that.data.globalParams.push({
that._data.globalParams.push({
describe: $inputs.eq(0).val(),
keyName: $inputs.eq(1).val(),
value: $inputs.eq(2).val()
});
}
});
localStorage['amWikiGlobalParam'] = JSON.stringify(that.data.globalParams);
localStorage['amWikiGlobalParam'] = JSON.stringify(that._data.globalParams);
$testingGlobal.hide();
}
});
@ -317,20 +348,23 @@
}
});
$('#testingGlobalWorking').on('click', function () {
if (that.data.globalParamWorking) {
that.data.globalParamWorking = false;
if (that._data.globalParamWorking) {
that._data.globalParamWorking = false;
localStorage['amWikiGParamWorking'] = 'off';
$(this).addClass('off');
} else {
that.data.globalParamWorking = true;
that._data.globalParamWorking = true;
localStorage['amWikiGParamWorking'] = 'on';
$(this).removeClass('off');
}
}).addClass(this.data.globalParamWorking ? '' : 'off');
}).addClass(this._data.globalParamWorking ? '' : 'off');
};
//发送请求
Testing.prototype.bindAjaxSend = function () {
/**
* 发送请求操作
* @private
*/
Testing.prototype._bindAjaxSend = function () {
var that = this;
var frame = $('#testingResponse')[0];
var $duration = $('#testingDuration'); //耗时输出
@ -347,17 +381,17 @@
});
}
//全局参数
if (that.data.globalParams.length > 0 && that.data.globalParamWorking) {
for (var i = 0; i < that.data.globalParams.length; i++) {
realParam[that.data.globalParams[i].keyName] = that.data.globalParams[i].value;
if (that._data.globalParams.length > 0 && that._data.globalParamWorking) {
for (var i = 0; i < that._data.globalParams.length; i++) {
realParam[that._data.globalParams[i].keyName] = that._data.globalParams[i].value;
}
}
frame.contentWindow.location.reload(); //刷新iframe以便重新输出内容
$loading.show();
var startTime = Date.now();
$.ajax({
type: that.request.method,
url: that.request.url,
type: that._request.method,
url: that._request.url,
data: realParam,
dataType: 'text',
success: function (data) {
@ -413,6 +447,15 @@
});
};
/**
* 判断测试面板是否处于打开状态
* @returns {Boolean}
* @public
*/
Testing.prototype.isOpen = function () {
return this.$e.testingShow.hasClass('on');
};
return win.AWTesting = Testing;
})(window, jQuery);

View File

@ -1,5 +1,5 @@
/**
* @desc amWiki Web端·工具集
* amWiki Web端·工具集
* @author Tevin
*/
@ -11,9 +11,9 @@
return win.tools = {
/**
* @desc 获取url参数
* @param name {string}
* @returns {string|null} - 获取的参数
* 获取url参数
* @param {String} name
* @returns {String|Null} - 获取的参数
*/
getURLParameter: function (name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
@ -26,10 +26,10 @@
},
/**
* @desc 转换字符中每个汉字为两个字符
* @param str {string} - 要编码的字符串
* @param [mod] {string} - 编码模式选择可选normal(默认)一个汉字对应两位字符short一个汉字对应一位字符
* @returns {string} - 编码后的字符串
* 转换字符中每个汉字为两个字符
* @param {String} str - 要编码的字符串
* @param {String} [mod] - 编码模式选择可选normal(默认)一个汉字对应两位字符short一个汉字对应一位字符
* @returns {String} 编码后的字符串
*/
simString: function (str, mod) {
mod = mod == 'short'; //短字符串
@ -59,12 +59,12 @@
},
/**
* @desc json格式化
* @param str {string} - 需要格式化的json字符串
* @returns {string} - 格式化后的json字符串
* json格式化
* @param {String} str - 需要格式化的json字符串
* @returns {String} 格式化后的json字符串
*/
formatJson: function (str) {
var json = decodeURI(str);
var json = decodeURI(str.replace(/%([^0-9A-Z]{0,2})/g, '%25$1'));
var reg = null,
formatted = '',
pad = 0,
@ -130,9 +130,9 @@
},
/**
* @desc 时间戳格式化为日期时间
* @param timestamp {number} - 时间戳
* @returns {string} - 日期时间
* 时间戳格式化为日期时间
* @param {Number} timestamp - 时间戳
* @returns {String} 日期时间
*/
formatTime: function (timestamp) {
var time = new Date(timestamp);

View File

@ -10,6 +10,7 @@
<link rel="stylesheet" type="text/css" href="amWiki/css/markdownbody.github.css" />
<link rel="stylesheet" type="text/css" href="amWiki/css/lhjs.github-gist.css" />
<link rel="stylesheet" type="text/css" href="amWiki/css/amWiki.css" />
{{custom.css}}
</head>
<body>
@ -21,7 +22,7 @@
<b>{{name}}</b>
<span>{{version}}</span>
</a>
<div class="menu_icon">
<div class="menu-icon" id="menuIcon">
<svg>
<use xlink:href="#icon:navStart"></use>
</svg>
@ -29,8 +30,8 @@
</div>
</header>
<!-- 页面 -->
<div class="container">
<div class="nav">
<div class="container" id="container">
<div class="nav" id="nav">
<div class="menu-filter">
<svg>
<use xlink:href="#icon:navFilter"></use>
@ -43,96 +44,142 @@
<use xlink:href="#icon:search"></use>
</svg>
</div>
<!--<div class="menu-fold" title="展开/折叠导航栏所有菜单">
<svg>
<use xlink:href="#navFolder1"></use>
</svg>
</div>-->
<nav class="menubar" id="menuBar"></nav>
<nav class="menubar scroller" id="menuBar" data-x-rolling="true">
<div class="scroller-inner">
<div class="scroller-content"></div>
</div>
</nav>
</div>
<div class="main" id="main">
<article class="markdown-body" id="view"></article>
<!-- 上下翻页 -->
<div class="main-sibling" id="mainSibling">
<p><span>上一篇:</span>
<a href="#"></a>
</p>
<p><span>下一篇:</span>
<a href="#"></a>
</p>
</div>
<!-- 全库搜索 -->
<div class="search-box" id="searchBox">
<div class="search-update">
<input type="button" id="searchUpdate" value="更新所有缓存" />
<div class="text">
<p>上次完全更新:<time id="cacheLasttime">0000-00-00 00:00:00</time></p>
<p>文档总数:<b id="cacheDocTotal">0</b> / <span>已缓存:<i id="cacheState">0%</i></span></p>
<div class="main scroller" id="main">
<div class="main-inner scroller-inner">
<!-- 正文 -->
<article class="markdown-body scroller-content" id="view"></article>
<script id="template:footnote" type="text/html">
<sup><a href="#fn:foot{{index}}" name="fn:note{{index}}" title="{{title}}">[{{index}}]</a></sup>
<ol class="footnote"><i>[参考资料]</i>{{list}}</ol>
<li id="fn:foot{{index}}">{{content}} {{back}}</li>
<li class="footnote-none" data-msg="匹配缺失">{{content}}</li>
<a href="#fn:note{{index}}">
<svg>
<use xlink:href="#icon:footnoteBack"></use>
</svg>
</a>
</script>
<!-- 上下翻页 -->
<div class="main-sibling scroller-content" id="mainSibling">
<p><span>上一篇:</span>
<a href="#"></a>
</p>
<p><span>下一篇:</span>
<a href="#"></a>
</p>
</div>
<!-- 全库搜索 -->
<div class="search-box" id="searchBox">
<div class="search-update">
<input type="button" id="searchUpdate" value="更新所有缓存" />
<div class="text">
<p>上次完全更新:<time id="cacheLasttime">0000-00-00 00:00:00</time></p>
<p>文档总数:<b id="cacheDocTotal">0</b> <i>/</i> <span>已缓存:<i id="cacheState">0%</i></span></p>
</div>
</div>
<h2>全库搜索</h2>
<div class="search-input">
<input type="text" id="searchText" placeholder="请输入搜索内容" />
<input type="button" id="search" value="搜 索" />
</div>
<div class="search-results" id="results">
<div class="search-result-message" id="resultMsg"></div>
<ul class="search-list"></ul>
<div class="search-result-more" id="resultMore">显示更多</div>
<script id="template:searchResult" type="text/html">
<li class="search-item">
<a href="?file={{path}}">
<div class="content">
<strong>{{title}}</strong> {{api}} {{content}}
</div>
<div class="bottom">
<p>位置:<span>{{path}}</span></p>
<div class="time">最后缓存于
<time>{{time}}</time>
</div>
</div>
</a>
</li>
</script>
</div>
</div>
<h2>全库搜索</h2>
<div class="search-input">
<input type="text" id="searchText" placeholder="请输入搜索内容" />
<input type="button" id="search" value="搜 索"/>
</div>
<div class="search-results" id="results">
<div class="search-result-message" id="resultMsg"></div>
<ul class="search-list"></ul>
<div class="search-result-more" id="resultMore">显示更多</div>
<script id="template:searchResult" type="text/html">
<li class="search-item">
<a href="?file={{path}}">
<div class="content">
<strong>{{title}}</strong>
{{api}}
{{content}}
</div>
<div class="bottom">
<p>位置:<span>{{path}}</span></p>
<div class="time">最后缓存于
<time>{{time}}</time>
</div>
</div>
</a>
</li>
</script>
</div>
<!-- 接口测试 -->
{{amWiki.testing.tpl}}
</div>
<!-- 接口测试 -->
{{amWiki.testing.tpl}}
</div>
</div>
<!-- 签名 -->
<aside>
<!-- 侧边目录 -->
<div class="contents" id="contents">
<div class="btn">
<svg>
<use xlink:href="#icon:contents"></use>
</svg>
<span>目录</span>
</div>
<div class="contents-list">
<p>
<a id="contentsTitle" href="#客户日志流水接口示例">客户日志流水接口示例</a>
</p>
<div class="markdown-contents"></div>
</div>
</div>
<!-- 返回顶部 -->
<div class="back-top" id="backTop">
<a class="back-icon" href="#">
<svg>
<use xlink:href="#icon:backTop"></use>
</svg>
</a>
<a class="back-text" href="#">返回顶部</a>
</div>
</aside>
<!-- 图片预览 -->
<section class="imgsv" id="imgsView">
<div class="imgsv-background"></div>
<div class="imgsv-view" id="imgsViewInner"></div>
<ul class="imgsv-panel">
<li class="prev off">Prev</li>
<li class="next">Next</li>
<li class="original">1:1</li>
<li class="suit">适合</li>
</ul>
</section>
<footer>
<!-- 签名 -->
<div class="signature">Powered by
<a href="https://github.com/TevinLi/amWiki" target="_blank">amWiki</a>
</div>
</footer>
<!-- 返回顶部 -->
<aside>
<div class="back-top">
<a href="#" class="back-icon">
<svg>
<use xlink:href="#backTop"></use>
</svg>
</a>
<a href="#" class="back-text">返回顶部</a>
</div>
</aside>
<!-- mounts -->
<div class="hidden" aw-include="mountLinks">
<script>{{config}}</script>
</div>
<!-- js -->
<div class="hidden">
<script type="text/javascript" src="amWiki/js/gbk.js"></script>
<script type="text/javascript" src="amWiki/js/pinyin.js"></script>
<script type="text/javascript" src="amWiki/js/jquery-compat-3.1.0.min.js"></script>
<script type="text/javascript" src="amWiki/js/marked.min.js"></script>
<script type="text/javascript" src="amWiki/js/highlight.min.js"></script>
<script type="text/javascript" src="amWiki/js/highlight.pack.js"></script>
<script type="text/javascript" src="amWiki/js/raphael-min.js"></script>
<script type="text/javascript" src="amWiki/js/flowchart.min.js"></script>
<script type="text/javascript" src="amWiki/js/amWiki.tools.js"></script>
<script type="text/javascript" src="amWiki/js/amWiki.storage.js"></script>
<script type="text/javascript" src="amWiki/js/amWiki.search.js"></script>
<script type="text/javascript" src="amWiki/js/amWiki.search.worker.js"></script>
<script type="text/javascript" src="amWiki/js/amWiki.docs.js"></script>
{{amWiki.testing.js}}
<script type="text/javascript" src="amWiki/js/amWiki.scrollbar.js"></script>
<script type="text/javascript" src="amWiki/js/amWiki.imgsView.js"></script>
<script type="text/javascript" src="amWiki/js/amWiki.js"></script>
{{custom.js}}
</div>
<!-- svg -->
<div class="hidden" id="svgSymbols"></div>

View File

@ -4,12 +4,21 @@
amWiki 是一款基于 Javascript 脚本语言、依赖 Atom 编辑器、使用 Markdown 标记语法的轻量级开源 wiki 文库系统。
amWiki 致力于让大家可以更简单、更便捷的建设个人和团队文库系统!
**传送门**
[amWiki 项目 Github 托管地址](https://github.com/TevinLi/amWiki "amWiki项目Github托管地址")
[![amWiki github star](https://amwiki.xf09.net/githubicons/Tevinli/amWiki/star) ![amWiki github fork](https://amwiki.xf09.net/githubicons/Tevinli/amWiki/fork)](https://github.com/TevinLi/amWiki)
[[view amWiki on Github](https://github.com/TevinLi/amWiki)]
GitHub:
[![](https://img.shields.io/github/stars/TevinLi/amWiki.svg?style=social&label=Star)](https://github.com/TevinLi/amWiki "GitHub Stars")
[![](https://img.shields.io/github/forks/TevinLi/amWiki.svg?style=social&label=Fork)](https://github.com/TevinLi/amWiki "GitHub Forks")
[![](https://img.shields.io/github/issues-raw/TevinLi/amWiki.svg)](https://github.com/TevinLi/amWiki "GitHub Open Issues")
[![](https://img.shields.io/github/issues-closed-raw/TevinLi/amWiki.svg)](https://github.com/TevinLi/amWiki "GitHub Closed Issues")
[![](https://img.shields.io/github/contributors/TevinLi/amWiki.svg)](https://github.com/TevinLi/amWiki "GitHub Contributors")
Apm:
[![apm](https://img.shields.io/apm/v/amWiki.svg)](https://atom.io/packages/amWiki "Apm Version")
[![apm](https://img.shields.io/apm/dm/amWiki.svg)](https://atom.io/packages/amWiki "Apm Downloads")
[![apm](https://img.shields.io/apm/l/amWiki.svg)](https://atom.io/packages/amWiki "MIT License")
## amWiki 优势
- 文档系统采用 Markdown 语法 [>>Markdown 快速开始](?file=001-学习amWiki/11-Markdown快速开始)
- 文档系统采用 Markdown 语法 [>>Markdown 快速开始](?file=001-学习amWiki/05-学习markdown/01-Markdown快速开始)
- 无需服务端开发,只需支持 http 访问的静态网页空间
- 不使用数据库,使用 `.md` 扩展名存储文档为本地文件
- 一键创建新文库,自动生成一套 Html 页面

View File

@ -5,6 +5,6 @@
![欢迎使用amWiki](amWiki/images/logo.png "欢迎使用amWiki")
### 欢迎使用 amWiki 轻文库!
—— **amWiki 致力于让大家可以更简单、更便捷的建设个人和团队文库系统**
—— **amWiki 致力于让大家可以更简单、更便捷的建设个人和团队文库**
[ [view amWiki on Github](https://github.com/TevinLi/amWiki) ]
[ [amWiki 官网文档中心](http://amwiki.org/doc/) ]

View File

@ -15,7 +15,7 @@
1. [【分割线】](#【分割线】 "【分割线】")
1. [【换行】](#【换行】 "【换行】")
1. [【html】](#【html】 "【html】")
1. [研究更多 markdown 语法详细细节](#研究更多markdown语法详细细节 "研究更多 markdown 语法详细细节")
1. [研究更多 markdown 语法详细细节](#研究更多 markdown 语法详细细节 "研究更多 markdown 语法详细细节")
## 简介

View File

@ -1,8 +1,8 @@
/**//*
/**
** ==================================================================================================
** 类名J.gbk
** 版本1.0
** 功能URLEncode,URLDecode
** 版本1.0
** 功能encode,decode
** 示例
---------------------------------------------------------------------------------------------------
@ -13,7 +13,7 @@
** 邮件zjfeihu@126.com
** 创建2011/09/15
** 更新2011/09/16
** 组件地址http://www.1kjs.com/lib/widget/gbk/
** 组件地址http://www.1kjs.com/lib/widget/gbk/
** ==================================================================================================
**/
GBK = function () {
@ -61,7 +61,7 @@ GBK = function () {
return encodeURIComponent(a);
} else {
var key = code.toString(16);
if (key.length != 4)key = ('000' + key).match(/....$/)[0];
if (key.length != 4) key = ('000' + key).match(/....$/)[0];
return U2Ghash[key] || a;
}
});

File diff suppressed because one or more lines are too long

15
files/highlight.pack.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -13,7 +13,7 @@
<path d="M959.391517 255.877715 511.696078 703.573153 64.00064 255.877715Z"></path>
</g>
</symbol>
<symbol id="backTop" viewBox="0 0 32 32" style="fill:currentColor">
<symbol id="icon:backTop" viewBox="0 0 32 32" style="fill:currentColor">
<g transform="scale(0.03125, 0.03125)">
<path d="M849.513502 524.698207l-308.405045-310.430168c-6.833639-6.867408-15.761985-10.1645-24.656563-10.095939-1.201361-0.103354-2.231831-0.686638-3.432169-0.686638-5.015224 0-9.68457 1.167592-13.978363 3.090385-4.325515 1.647523-8.445346 4.155646-11.948123 7.658424l-278.290167 277.807167c-13.426801 13.392008-13.46057 35.130102-0.034792 48.556902 6.695492 6.729262 15.487739 10.096962 24.312732 10.096962 8.7902 0 17.546631-3.367701 24.27794-10.028401l221.320665-220.97581 0 604.927459c0 18.99154 15.384385 34.340109 34.340109 34.340109 18.952654 0 34.340109-15.34857 34.340109-34.340109L547.359834 318.007751l253.460666 255.111259c6.696516 6.730285 15.557324 10.129708 24.347524 10.129708s17.512862-3.331885 24.208355-9.994632C862.872764 559.897893 862.90551 538.123984 849.513502 524.698207zM890.757858 134.805242 135.282614 134.805242c-18.954701 0-34.340109-15.384385-34.340109-34.340109s15.384385-34.340109 34.340109-34.340109l755.475244 0c18.98847 0 34.339086 15.384385 34.339086 34.340109S909.745305 134.805242 890.757858 134.805242z"></path>
</g>
@ -28,16 +28,6 @@
<path d="M588.99845 509.618605l412.775194-412.775194c19.051163-19.051163 19.051163-57.153488 0-82.555039-19.051163-19.051163-57.153488-19.051163-82.555039 0L512.793798 427.063566 100.018605 20.63876c-19.051163-19.051163-57.153488-19.051163-82.555039 0-19.051163 19.051163-19.051163 57.153488 0 82.555039L430.23876 509.618605l-412.775194 412.775194c-19.051163 19.051163-19.051163 57.153488 0 82.555039C30.164341 1011.299225 49.215504 1017.649612 61.916279 1017.649612s31.751938-6.350388 38.102326-19.051163L512.793798 592.173643l412.775194 412.775194c12.700775 12.700775 25.40155 19.051163 38.102326 19.051163 12.700775 0 31.751938-6.350388 38.102326-19.051163 19.051163-19.051163 19.051163-57.153488 0-82.555039L588.99845 509.618605z"></path>
</g>
</symbol>
<symbol id="navFolder1" viewBox="0 0 32 32" style="fill:currentColor">
<g transform="scale(0.03125, 0.03125)">
<path d="M853.333333 260.352 512 260.352 512 192c0-35.328-28.672-64-64-64L170.666667 128c-47.104 0-85.333333 38.229333-85.333333 85.333333l0 132.352L85.333333 810.666667l0 0.085333c0 46.933333 38.4 85.333333 85.333333 85.333333l682.666667 0c46.933333 0 85.333333-38.4 85.333333-85.333333l0-465.066667C938.666667 298.752 900.266667 260.352 853.333333 260.352zM874.666667 789.418667c0 23.466667-19.2 42.666667-42.666667 42.666667L192 832.085333c-23.466667 0-42.666667-19.2-42.666667-42.666667l0-422.4L149.333333 263.082667 149.333333 234.666667c0-23.552 19.114667-42.666667 42.666667-42.666667l213.333333 0c23.552 0 42.666667 19.114667 42.666667 42.666667l0 89.685333 384 0c23.466667 0 42.666667 19.2 42.666667 42.666667L874.666667 789.418667z"></path>
</g>
</symbol>
<symbol id="navFolder2" viewBox="0 0 32 32" style="fill:currentColor">
<g transform="scale(0.03125, 0.03125)">
<path d="M950.048 530.016c0-12-10.016-18.016-28-18.016l-580 0c-14.016 0-30.016 4-46.016 12s-30.016 16-38.016 28l-158.016 194.016c-6.016 8-8 14.016-8 20 0 12 8 20 28 20l580 0c14.016 0 30.016-4 46.016-12s28-18.016 38.016-28l156-194.016c6.016-8 10.016-16 10.016-22.016zM342.048 444l408 0 0-86.016c0-14.016-4-26.016-14.016-36s-22.016-14.016-36-14.016l-308 0c-14.016 0-26.016-6.016-36-16s-14.016-22.016-14.016-36l0-34.016c0-14.016-6.016-26.016-16-36s-22.016-16-36-16l-170.016 0c-14.016 0-26.016 6.016-36 16s-16 22.016-16 36l0 454.016 136-168c16-18.016 38.016-34.016 62.016-46.016 26.016-12 52-18.016 76-18.016zM1018.048 530.016c0 22.016-8 44-24 64l-158.016 194.016c-16 20-36 34.016-62.016 46.016-26.016 14.016-50.016 20-74.016 20l-580 0c-34.016 0-62.016-12-84-36-24-24-36-52-36-84l0-512c0-32 12-60 36-84 22.016-24 50.016-36 84-36l170.016 0c32 0 60 12 84 36s36 52 36 84l0 16 290.016 0c32 0 60 12 84 36s36 52 36 84l0 86.016 102.016 0c18.016 0 36 4 52 12 16 10.016 28 22.016 36 38.016 6.016 12 8 24 8 36z"></path>
</g>
</symbol>
<symbol id="icon:linkAnchor" viewBox="0 0 32 32" style="fill:currentColor">
<g transform="scale(0.03125, 0.03125)">
<path d="M616.601336 477.134935c0-96.148827-76.528977-174.34477-170.628122-174.34477l-138.339742 0.027629c-98.788957 0.007163-179.177845 78.195943-179.177845 174.330444l0.017396 69.698408c-0.024559 96.15292 80.406285 174.359097 179.205475 174.359097l38.696324 0.00307c19.815301-0.024559 35.851533-15.624862 35.848463-34.882462 0.00307-19.250436-16.032138-34.853809-35.823903-34.853809l-38.749536 0c-59.268872-0.007163-109.451808-46.925752-109.465111-104.604406l0-69.733201c0-57.668421 50.154283-104.58394 109.462041-104.594173l138.329509-0.024559c56.472176 0.024559 100.867292 46.911426 100.890828 104.618732l1.496074 34.872229c0 9.627265 3.810792 18.365276 9.969049 24.65554 6.178723 6.310729 14.728446 10.209526 24.153096 10.206456 18.84623 0.00307 34.118052-15.600303 34.118052-34.860972L616.601336 477.134935zM895.526977 477.152331c0.024559-96.148827-80.406285-174.355003-179.205475-174.359097l-38.696324-0.00307c-19.815301 0.024559-35.851533 15.627932-35.851533 34.882462 0 19.254529 16.036231 34.853809 35.826973 34.857902l38.745443 0c59.268872 0.00307 109.451808 46.922682 109.469204 104.604406l0 69.733201c0 57.668421-50.154283 104.58394-109.462041 104.591103l-138.329509 0.027629c-56.472176-0.024559-100.867292-46.911426-100.890828-104.618732l-1.496074-34.872229c0-9.630335-3.810792-18.365276-9.969049-24.65554-6.181793-6.314823-14.728446-10.209526-24.153096-10.205433-18.84623-0.007163-34.119075 15.600303-34.119075 34.860972l0 34.872229c0 96.145757 76.532047 174.340677 170.631192 174.340677l138.339742-0.024559c98.788957-0.007163 179.177845-78.199013 179.173752-174.330444L895.526977 477.152331z"></path>
@ -53,4 +43,14 @@
<path d="M941.427376 910.59516c-16.080234 16.010649-69.712735 18.382673-85.792968 2.378164L631.709429 684.444491c61.748343-50.087768 70.796416-148.074454 70.796416-199.626573 0-148.759045-109.305475-260.410961-257.868045-260.410961-148.56871 0-256.612449 109.908202-256.612449 258.673387 0 148.752906 102.096283 261.730003 250.664993 261.730003 30.759561 0 44.760436 0.037862 72.293513-9.466606l0.38681 1.483794c7.000438-3.544733 77.435627-12.098549 77.435627 60.962443 0 20.455891-15.623839 41.425482-34.932604 48.178279-32.66905 11.420097-75.431993 16.936741-108.60144 16.936741-211.592093 0-381.180536-169.367409-381.180536-380.807029 0-211.370035 169.589466-382.79122 381.180536-382.79122 211.667817 0 381.180536 171.421184 381.180536 382.79122 0 65.812915-12.979616 127.682008-40.201607 179.176822l157.154249 159.189605C959.423239 836.475046 957.439048 894.660236 941.427376 910.59516L941.427376 910.59516zM941.427376 910.59516"></path>
</g>
</symbol>
<symbol id="icon:contents" viewBox="0 0 32 32" style="fill:currentColor">
<g transform="scale(0.03125, 0.03125)">
<path d="M173.311 317.051l677.38 0c20.782 0 37.632-22.186 37.632-49.552 0-27.363-16.85-49.551-37.632-49.551l-677.38 0c-20.782 0-37.633 22.188-37.633 49.551C135.678 294.865 152.528 317.051 173.311 317.051zM850.69 482.948l-677.38 0c-20.782 0-37.633 22.188-37.633 49.551 0 27.366 16.851 49.553 37.633 49.553l677.38 0c20.782 0 37.632-22.187 37.632-49.553C888.322 505.136 871.473 482.948 850.69 482.948zM865.307 706.948l-414.29 0c-12.71 0-23.017 22.188-23.017 49.551 0 27.366 10.307 49.553 23.017 49.553l414.29 0c12.71 0 23.016-22.187 23.016-49.553C888.322 729.136 878.017 706.948 865.307 706.948z"></path>
</g>
</symbol>
<symbol id="icon:footnoteBack" viewBox="0 0 32 32" style="fill:currentColor">
<g transform="scale(0.03125, 0.03125)">
<path transform="rotate(-118.97699737548828 544.0010986328125,517.0001831054688)" d="m679.72461,226.14308l0,-218.14282l271.44708,290.8571l-271.44708,290.85712l0,-218.14285c0,0 -407.17047,0 -407.17047,218.14285s339.30872,290.85706 339.30872,290.85706l0,145.42859c0,0 -475.03235,-72.71429 -475.03235,-436.28564c0.00006,-290.85712 339.30887,-363.5714 542.8941,-363.5714z"/>
</g>
</symbol>
</svg>

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -12,7 +12,7 @@ dfn{font-style:italic}
h1{font-size:2em;margin:0.67em 0}
mark{background:#ff0;color:#000}
small{font-size:80%}
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
sub,sup{line-height:0;position:relative;vertical-align:baseline}
sup{top:-0.5em}
sub{bottom:-0.25em}
img{border:0}

457
files/pinyin.js Normal file
View File

@ -0,0 +1,457 @@
/**
* Convert Chinese to Pinyin
* 未知作者
* 不能识别多音字
*/
var PinYin = (function () {
var py = {
"a": "\u554a\u963f\u9515",
"ai": "\u57c3\u6328\u54ce\u5509\u54c0\u7691\u764c\u853c\u77ee\u827e\u788d\u7231\u9698\u8bf6\u6371\u55f3\u55cc\u5ad2\u7477\u66a7\u7839\u953f\u972d",
"an": "\u978d\u6c28\u5b89\u4ffa\u6309\u6697\u5cb8\u80fa\u6848\u8c19\u57ef\u63de\u72b4\u5eb5\u6849\u94f5\u9e4c\u9878\u9eef",
"ang": "\u80ae\u6602\u76ce",
"ao": "\u51f9\u6556\u71ac\u7ff1\u8884\u50b2\u5965\u61ca\u6fb3\u5773\u62d7\u55f7\u5662\u5c99\u5ed2\u9068\u5aaa\u9a9c\u8071\u87af\u93ca\u9ccc\u93d6",
"ba": "\u82ad\u634c\u6252\u53ed\u5427\u7b06\u516b\u75a4\u5df4\u62d4\u8dcb\u9776\u628a\u8019\u575d\u9738\u7f62\u7238\u8307\u83dd\u8406\u636d\u5c9c\u705e\u6777\u94af\u7c91\u9c85\u9b43",
"bai": "\u767d\u67cf\u767e\u6446\u4f70\u8d25\u62dc\u7a17\u859c\u63b0\u97b4",
"ban": "\u6591\u73ed\u642c\u6273\u822c\u9881\u677f\u7248\u626e\u62cc\u4f34\u74e3\u534a\u529e\u7eca\u962a\u5742\u8c73\u94a3\u7622\u764d\u8228",
"bang": "\u90a6\u5e2e\u6886\u699c\u8180\u7ed1\u68d2\u78c5\u868c\u9551\u508d\u8c24\u84a1\u8783",
"bao": "\u82de\u80de\u5305\u8912\u96f9\u4fdd\u5821\u9971\u5b9d\u62b1\u62a5\u66b4\u8c79\u9c8d\u7206\u52f9\u8446\u5b80\u5b62\u7172\u9e28\u8913\u8db5\u9f85",
"bo": "\u5265\u8584\u73bb\u83e0\u64ad\u62e8\u94b5\u6ce2\u535a\u52c3\u640f\u94c2\u7b94\u4f2f\u5e1b\u8236\u8116\u818a\u6e24\u6cca\u9a73\u4eb3\u8543\u5575\u997d\u6a97\u64d8\u7934\u94b9\u9e41\u7c38\u8ddb",
"bei": "\u676f\u7891\u60b2\u5351\u5317\u8f88\u80cc\u8d1d\u94a1\u500d\u72c8\u5907\u60eb\u7119\u88ab\u5b5b\u9642\u90b6\u57e4\u84d3\u5457\u602b\u6096\u789a\u9e4e\u8919\u943e",
"ben": "\u5954\u82ef\u672c\u7b28\u755a\u574c\u951b",
"beng": "\u5d29\u7ef7\u752d\u6cf5\u8e66\u8ff8\u552a\u5623\u750f",
"bi": "\u903c\u9f3b\u6bd4\u9119\u7b14\u5f7c\u78a7\u84d6\u853d\u6bd5\u6bd9\u6bd6\u5e01\u5e87\u75f9\u95ed\u655d\u5f0a\u5fc5\u8f9f\u58c1\u81c2\u907f\u965b\u5315\u4ef3\u4ffe\u8298\u835c\u8378\u5421\u54d4\u72f4\u5eb3\u610e\u6ed7\u6fde\u5f3c\u59a3\u5a62\u5b16\u74a7\u8d32\u7540\u94cb\u79d5\u88e8\u7b5a\u7b85\u7be6\u822d\u895e\u8df8\u9ac0",
"bian": "\u97ad\u8fb9\u7f16\u8d2c\u6241\u4fbf\u53d8\u535e\u8fa8\u8fa9\u8fab\u904d\u533e\u5f01\u82c4\u5fed\u6c74\u7f0f\u7178\u782d\u78a5\u7a39\u7a86\u8759\u7b3e\u9cca",
"biao": "\u6807\u5f6a\u8198\u8868\u5a4a\u9aa0\u98d1\u98d9\u98da\u706c\u9556\u9573\u762d\u88f1\u9cd4",
"bie": "\u9cd6\u618b\u522b\u762a\u8e69\u9cd8",
"bin": "\u5f6c\u658c\u6fd2\u6ee8\u5bbe\u6448\u50a7\u6d5c\u7f24\u73a2\u6ba1\u8191\u9554\u9acc\u9b13",
"bing": "\u5175\u51b0\u67c4\u4e19\u79c9\u997c\u70b3\u75c5\u5e76\u7980\u90b4\u6452\u7ee0\u678b\u69df\u71f9",
"bu": "\u6355\u535c\u54fa\u8865\u57e0\u4e0d\u5e03\u6b65\u7c3f\u90e8\u6016\u62ca\u535f\u900b\u74ff\u6661\u949a\u91ad",
"ca": "\u64e6\u5693\u7924",
"cai": "\u731c\u88c1\u6750\u624d\u8d22\u776c\u8e29\u91c7\u5f69\u83dc\u8521",
"can": "\u9910\u53c2\u8695\u6b8b\u60ed\u60e8\u707f\u9a96\u74a8\u7cb2\u9eea",
"cang": "\u82cd\u8231\u4ed3\u6ca7\u85cf\u4f27",
"cao": "\u64cd\u7cd9\u69fd\u66f9\u8349\u8279\u5608\u6f15\u87ac\u825a",
"ce": "\u5395\u7b56\u4fa7\u518c\u6d4b\u5202\u5e3b\u607b",
"ceng": "\u5c42\u8e6d\u564c",
"cha": "\u63d2\u53c9\u832c\u8336\u67e5\u78b4\u643d\u5bdf\u5c94\u5dee\u8be7\u7339\u9987\u6c4a\u59f9\u6748\u6942\u69ce\u6aab\u9497\u9538\u9572\u8869",
"chai": "\u62c6\u67f4\u8c7a\u4faa\u8308\u7625\u867f\u9f87",
"chan": "\u6400\u63ba\u8749\u998b\u8c17\u7f20\u94f2\u4ea7\u9610\u98a4\u5181\u8c04\u8c36\u8487\u5edb\u5fcf\u6f7a\u6fb6\u5b71\u7fbc\u5a75\u5b17\u9aa3\u89c7\u7985\u9561\u88e3\u87fe\u8e94",
"chang": "\u660c\u7316\u573a\u5c1d\u5e38\u957f\u507f\u80a0\u5382\u655e\u7545\u5531\u5021\u4f25\u9b2f\u82cc\u83d6\u5f9c\u6005\u60dd\u960a\u5a3c\u5ae6\u6636\u6c05\u9cb3",
"chao": "\u8d85\u6284\u949e\u671d\u5632\u6f6e\u5de2\u5435\u7092\u600a\u7ec9\u6641\u8016",
"che": "\u8f66\u626f\u64a4\u63a3\u5f7b\u6f88\u577c\u5c6e\u7817",
"chen": "\u90f4\u81e3\u8fb0\u5c18\u6668\u5ff1\u6c89\u9648\u8d81\u886c\u79f0\u8c0c\u62bb\u55d4\u5bb8\u741b\u6987\u809c\u80c2\u789c\u9f80",
"cheng": "\u6491\u57ce\u6a59\u6210\u5448\u4e58\u7a0b\u60e9\u6f84\u8bda\u627f\u901e\u9a8b\u79e4\u57d5\u5d4a\u5fb5\u6d48\u67a8\u67fd\u6a18\u665f\u584d\u77a0\u94d6\u88ce\u86cf\u9172",
"chi": "\u5403\u75f4\u6301\u5319\u6c60\u8fdf\u5f1b\u9a70\u803b\u9f7f\u4f88\u5c3a\u8d64\u7fc5\u65a5\u70bd\u50ba\u5880\u82aa\u830c\u640b\u53f1\u54e7\u557b\u55e4\u5f73\u996c\u6cb2\u5ab8\u6555\u80dd\u7719\u7735\u9e31\u761b\u892b\u86a9\u87ad\u7b1e\u7bea\u8c49\u8e05\u8e1f\u9b51",
"chong": "\u5145\u51b2\u866b\u5d07\u5ba0\u833a\u5fe1\u61a7\u94f3\u825f",
"chou": "\u62bd\u916c\u7574\u8e0c\u7a20\u6101\u7b79\u4ec7\u7ef8\u7785\u4e11\u4fe6\u5733\u5e31\u60c6\u6eb4\u59af\u7633\u96e0\u9c8b",
"chu": "\u81ed\u521d\u51fa\u6a71\u53a8\u8e87\u9504\u96cf\u6ec1\u9664\u695a\u7840\u50a8\u77d7\u6410\u89e6\u5904\u4e8d\u520d\u61b7\u7ecc\u6775\u696e\u6a17\u870d\u8e70\u9edc",
"chuan": "\u63e3\u5ddd\u7a7f\u693d\u4f20\u8239\u5598\u4e32\u63be\u821b\u60f4\u9044\u5ddb\u6c1a\u948f\u9569\u8221",
"chuang": "\u75ae\u7a97\u5e62\u5e8a\u95ef\u521b\u6006",
"chui": "\u5439\u708a\u6376\u9524\u5782\u9672\u68f0\u69cc",
"chun": "\u6625\u693f\u9187\u5507\u6df3\u7eaf\u8822\u4fc3\u83bc\u6c8c\u80ab\u6710\u9e51\u877d",
"chuo": "\u6233\u7ef0\u851f\u8fb6\u8f8d\u955e\u8e14\u9f8a",
"ci": "\u75b5\u8328\u78c1\u96cc\u8f9e\u6148\u74f7\u8bcd\u6b64\u523a\u8d50\u6b21\u8360\u5472\u5d6f\u9e5a\u8785\u7ccd\u8d91",
"cong": "\u806a\u8471\u56f1\u5306\u4ece\u4e1b\u506c\u82c1\u6dd9\u9aa2\u742e\u7481\u679e",
"cu": "\u51d1\u7c97\u918b\u7c07\u731d\u6b82\u8e59",
"cuan": "\u8e7f\u7be1\u7a9c\u6c46\u64ba\u6615\u7228",
"cui": "\u6467\u5d14\u50ac\u8106\u7601\u7cb9\u6dec\u7fe0\u8403\u60b4\u7480\u69b1\u96b9",
"cun": "\u6751\u5b58\u5bf8\u78cb\u5fd6\u76b4",
"cuo": "\u64ae\u6413\u63aa\u632b\u9519\u539d\u811e\u9509\u77ec\u75e4\u9e7e\u8e49\u8e9c",
"da": "\u642d\u8fbe\u7b54\u7629\u6253\u5927\u8037\u54d2\u55d2\u601b\u59b2\u75b8\u8921\u7b2a\u977c\u9791",
"dai": "\u5446\u6b79\u50a3\u6234\u5e26\u6b86\u4ee3\u8d37\u888b\u5f85\u902e\u6020\u57ed\u7519\u5454\u5cb1\u8fe8\u902f\u9a80\u7ed0\u73b3\u9edb",
"dan": "\u803d\u62c5\u4e39\u5355\u90f8\u63b8\u80c6\u65e6\u6c2e\u4f46\u60ee\u6de1\u8bde\u5f39\u86cb\u4ebb\u510b\u5369\u840f\u5556\u6fb9\u6a90\u6b9a\u8d55\u7708\u7605\u8043\u7baa",
"dang": "\u5f53\u6321\u515a\u8361\u6863\u8c20\u51fc\u83ea\u5b95\u7800\u94db\u88c6",
"dao": "\u5200\u6363\u8e48\u5012\u5c9b\u7977\u5bfc\u5230\u7a3b\u60bc\u9053\u76d7\u53e8\u5541\u5fc9\u6d2e\u6c18\u7118\u5fd1\u7e9b",
"de": "\u5fb7\u5f97\u7684\u951d",
"deng": "\u8e6c\u706f\u767b\u7b49\u77aa\u51f3\u9093\u5654\u5d9d\u6225\u78f4\u956b\u7c26",
"di": "\u5824\u4f4e\u6ef4\u8fea\u654c\u7b1b\u72c4\u6da4\u7fdf\u5ae1\u62b5\u5e95\u5730\u8482\u7b2c\u5e1d\u5f1f\u9012\u7f14\u6c10\u7c74\u8bcb\u8c1b\u90b8\u577b\u839c\u837b\u5600\u5a23\u67e2\u68e3\u89cc\u7825\u78b2\u7747\u955d\u7f9d\u9ab6",
"dian": "\u98a0\u6382\u6ec7\u7898\u70b9\u5178\u975b\u57ab\u7535\u4f43\u7538\u5e97\u60e6\u5960\u6dc0\u6bbf\u4e36\u963d\u576b\u57dd\u5dc5\u73b7\u765c\u766b\u7c1f\u8e2e",
"diao": "\u7889\u53fc\u96d5\u51cb\u5201\u6389\u540a\u9493\u8c03\u8f7a\u94de\u8729\u7c9c\u8c82",
"die": "\u8dcc\u7239\u789f\u8776\u8fed\u8c0d\u53e0\u4f5a\u57a4\u581e\u63f2\u558b\u6e2b\u8f76\u7252\u74de\u8936\u800b\u8e40\u9cbd\u9cce",
"ding": "\u4e01\u76ef\u53ee\u9489\u9876\u9f0e\u952d\u5b9a\u8ba2\u4e22\u4ec3\u5576\u738e\u815a\u7887\u753a\u94e4\u7594\u8035\u914a",
"dong": "\u4e1c\u51ac\u8463\u61c2\u52a8\u680b\u4f97\u606b\u51bb\u6d1e\u578c\u549a\u5cbd\u5cd2\u5902\u6c21\u80e8\u80f4\u7850\u9e2b",
"dou": "\u515c\u6296\u6597\u9661\u8c46\u9017\u75d8\u8538\u94ad\u7aa6\u7aac\u86aa\u7bfc\u9161",
"du": "\u90fd\u7763\u6bd2\u728a\u72ec\u8bfb\u5835\u7779\u8d4c\u675c\u9540\u809a\u5ea6\u6e21\u5992\u828f\u561f\u6e0e\u691f\u6a50\u724d\u8839\u7b03\u9ad1\u9ee9",
"duan": "\u7aef\u77ed\u953b\u6bb5\u65ad\u7f0e\u5f56\u6934\u7145\u7c16",
"dui": "\u5806\u5151\u961f\u5bf9\u603c\u619d\u7893",
"dun": "\u58a9\u5428\u8e72\u6566\u987f\u56e4\u949d\u76fe\u9041\u7096\u7818\u7905\u76f9\u9566\u8db8",
"duo": "\u6387\u54c6\u591a\u593a\u579b\u8eb2\u6735\u8dfa\u8235\u5241\u60f0\u5815\u5484\u54da\u7f0d\u67c1\u94ce\u88f0\u8e31",
"e": "\u86fe\u5ce8\u9e45\u4fc4\u989d\u8bb9\u5a25\u6076\u5384\u627c\u904f\u9102\u997f\u5669\u8c14\u57a9\u57ad\u82ca\u83aa\u843c\u5443\u6115\u5c59\u5a40\u8f6d\u66f7\u816d\u786a\u9507\u9537\u9e57\u989a\u9cc4",
"en": "\u6069\u84bd\u6441\u5514\u55ef",
"er": "\u800c\u513f\u8033\u5c14\u9975\u6d31\u4e8c\u8d30\u8fe9\u73e5\u94d2\u9e38\u9c95",
"fa": "\u53d1\u7f5a\u7b4f\u4f10\u4e4f\u9600\u6cd5\u73d0\u57a1\u781d",
"fan": "\u85e9\u5e06\u756a\u7ffb\u6a0a\u77fe\u9492\u7e41\u51e1\u70e6\u53cd\u8fd4\u8303\u8d29\u72af\u996d\u6cdb\u8629\u5e61\u72ad\u68b5\u6535\u71d4\u7548\u8e6f",
"fang": "\u574a\u82b3\u65b9\u80aa\u623f\u9632\u59a8\u4eff\u8bbf\u7eba\u653e\u531a\u90a1\u5f77\u94ab\u822b\u9c82",
"fei": "\u83f2\u975e\u5561\u98de\u80a5\u532a\u8bfd\u5420\u80ba\u5e9f\u6cb8\u8d39\u82be\u72d2\u60b1\u6ddd\u5983\u7ecb\u7eef\u69a7\u8153\u6590\u6249\u7953\u7829\u9544\u75f1\u871a\u7bda\u7fe1\u970f\u9cb1",
"fen": "\u82ac\u915a\u5429\u6c1b\u5206\u7eb7\u575f\u711a\u6c7e\u7c89\u594b\u4efd\u5fff\u6124\u7caa\u507e\u7035\u68fc\u610d\u9cbc\u9f22",
"feng": "\u4e30\u5c01\u67ab\u8702\u5cf0\u950b\u98ce\u75af\u70fd\u9022\u51af\u7f1d\u8bbd\u5949\u51e4\u4ff8\u9146\u8451\u6ca3\u781c",
"fu": "\u4f5b\u5426\u592b\u6577\u80a4\u5b75\u6276\u62c2\u8f90\u5e45\u6c1f\u7b26\u4f0f\u4fd8\u670d\u6d6e\u6daa\u798f\u88b1\u5f17\u752b\u629a\u8f85\u4fef\u91dc\u65a7\u812f\u8151\u5e9c\u8150\u8d74\u526f\u8986\u8d4b\u590d\u5085\u4ed8\u961c\u7236\u8179\u8d1f\u5bcc\u8ba3\u9644\u5987\u7f1a\u5490\u5310\u51eb\u90db\u8299\u82fb\u832f\u83a9\u83d4\u544b\u5e5e\u6ecf\u8274\u5b5a\u9a78\u7ec2\u6874\u8d59\u9efb\u9efc\u7f58\u7a03\u99a5\u864d\u86a8\u8709\u8760\u876e\u9eb8\u8dba\u8dd7\u9cc6",
"ga": "\u5676\u560e\u86e4\u5c2c\u5477\u5c15\u5c1c\u65ee\u9486",
"gai": "\u8be5\u6539\u6982\u9499\u76d6\u6e89\u4e10\u9654\u5793\u6224\u8d45\u80f2",
"gan": "\u5e72\u7518\u6746\u67d1\u7aff\u809d\u8d76\u611f\u79c6\u6562\u8d63\u5769\u82f7\u5c34\u64c0\u6cd4\u6de6\u6f89\u7ec0\u6a44\u65f0\u77f8\u75b3\u9150",
"gang": "\u5188\u521a\u94a2\u7f38\u809b\u7eb2\u5c97\u6e2f\u6206\u7f61\u9883\u7b7b",
"gong": "\u6760\u5de5\u653b\u529f\u606d\u9f9a\u4f9b\u8eac\u516c\u5bab\u5f13\u5de9\u6c5e\u62f1\u8d21\u5171\u857b\u5efe\u54a3\u73d9\u80b1\u86a3\u86e9\u89e5",
"gao": "\u7bd9\u768b\u9ad8\u818f\u7f94\u7cd5\u641e\u9550\u7a3f\u544a\u777e\u8bf0\u90dc\u84bf\u85c1\u7f1f\u69d4\u69c1\u6772\u9506",
"ge": "\u54e5\u6b4c\u6401\u6208\u9e3d\u80f3\u7599\u5272\u9769\u845b\u683c\u9601\u9694\u94ec\u4e2a\u5404\u9b32\u4ee1\u54ff\u5865\u55dd\u7ea5\u643f\u8188\u784c\u94ea\u9549\u88bc\u988c\u867c\u8238\u9abc\u9ac2",
"gei": "\u7ed9",
"gen": "\u6839\u8ddf\u4e98\u831b\u54cf\u826e",
"geng": "\u8015\u66f4\u5e9a\u7fb9\u57c2\u803f\u6897\u54fd\u8d53\u9ca0",
"gou": "\u94a9\u52fe\u6c9f\u82df\u72d7\u57a2\u6784\u8d2d\u591f\u4f5d\u8bdf\u5ca3\u9058\u5abe\u7f11\u89cf\u5f40\u9e32\u7b31\u7bdd\u97b2",
"gu": "\u8f9c\u83c7\u5495\u7b8d\u4f30\u6cbd\u5b64\u59d1\u9f13\u53e4\u86ca\u9aa8\u8c37\u80a1\u6545\u987e\u56fa\u96c7\u560f\u8bc2\u83f0\u54cc\u5d2e\u6c69\u688f\u8f71\u726f\u727f\u80cd\u81cc\u6bc2\u77bd\u7f5f\u94b4\u9522\u74e0\u9e2a\u9e44\u75fc\u86c4\u9164\u89da\u9cb4\u9ab0\u9e58",
"gua": "\u522e\u74dc\u5250\u5be1\u6302\u8902\u5366\u8bd6\u5471\u681d\u9e39",
"guai": "\u4e56\u62d0\u602a\u54d9",
"guan": "\u68fa\u5173\u5b98\u51a0\u89c2\u7ba1\u9986\u7f50\u60ef\u704c\u8d2f\u500c\u839e\u63bc\u6dab\u76e5\u9e73\u9ccf",
"guang": "\u5149\u5e7f\u901b\u72b7\u6844\u80f1\u7592",
"gui": "\u7470\u89c4\u572d\u7845\u5f52\u9f9f\u95fa\u8f68\u9b3c\u8be1\u7678\u6842\u67dc\u8dea\u8d35\u523d\u5326\u523f\u5e8b\u5b84\u59ab\u6867\u7085\u6677\u7688\u7c0b\u9c91\u9cdc",
"gun": "\u8f8a\u6eda\u68cd\u4e28\u886e\u7ef2\u78d9\u9ca7",
"guo": "\u9505\u90ed\u56fd\u679c\u88f9\u8fc7\u9998\u8803\u57da\u63b4\u5459\u56d7\u5e3c\u5d1e\u7313\u6901\u8662\u951e\u8052\u872e\u873e\u8748",
"ha": "\u54c8",
"hai": "\u9ab8\u5b69\u6d77\u6c26\u4ea5\u5bb3\u9a87\u54b4\u55e8\u988f\u91a2",
"han": "\u9163\u61a8\u90af\u97e9\u542b\u6db5\u5bd2\u51fd\u558a\u7f55\u7ff0\u64bc\u634d\u65f1\u61be\u608d\u710a\u6c57\u6c49\u9097\u83e1\u6496\u961a\u701a\u6657\u7113\u9894\u86b6\u9f3e",
"hen": "\u592f\u75d5\u5f88\u72e0\u6068",
"hang": "\u676d\u822a\u6c86\u7ed7\u73e9\u6841",
"hao": "\u58d5\u568e\u8c6a\u6beb\u90dd\u597d\u8017\u53f7\u6d69\u8585\u55e5\u5686\u6fe0\u704f\u660a\u7693\u98a2\u869d",
"he": "\u5475\u559d\u8377\u83cf\u6838\u79be\u548c\u4f55\u5408\u76d2\u8c89\u9602\u6cb3\u6db8\u8d6b\u8910\u9e64\u8d3a\u8bc3\u52be\u58d1\u85ff\u55d1\u55ec\u9616\u76cd\u86b5\u7fee",
"hei": "\u563f\u9ed1",
"heng": "\u54fc\u4ea8\u6a2a\u8861\u6052\u8a07\u8605",
"hong": "\u8f70\u54c4\u70d8\u8679\u9e3f\u6d2a\u5b8f\u5f18\u7ea2\u9ec9\u8ba7\u836d\u85a8\u95f3\u6cd3",
"hou": "\u5589\u4faf\u7334\u543c\u539a\u5019\u540e\u5820\u5f8c\u9005\u760a\u7bcc\u7cc7\u9c8e\u9aba",
"hu": "\u547c\u4e4e\u5ffd\u745a\u58f6\u846b\u80e1\u8774\u72d0\u7cca\u6e56\u5f27\u864e\u552c\u62a4\u4e92\u6caa\u6237\u51b1\u553f\u56eb\u5cb5\u7322\u6019\u60da\u6d52\u6ef9\u7425\u69f2\u8f77\u89f3\u70c0\u7173\u623d\u6248\u795c\u9e55\u9e71\u7b0f\u9190\u659b",
"hua": "\u82b1\u54d7\u534e\u733e\u6ed1\u753b\u5212\u5316\u8bdd\u5290\u6d4d\u9a85\u6866\u94e7\u7a1e",
"huai": "\u69d0\u5f8a\u6000\u6dee\u574f\u8fd8\u8e1d",
"huan": "\u6b22\u73af\u6853\u7f13\u6362\u60a3\u5524\u75ea\u8c62\u7115\u6da3\u5ba6\u5e7b\u90c7\u5942\u57b8\u64d0\u571c\u6d39\u6d63\u6f36\u5bf0\u902d\u7f33\u953e\u9ca9\u9b1f",
"huang": "\u8352\u614c\u9ec4\u78fa\u8757\u7c27\u7687\u51f0\u60f6\u714c\u6643\u5e4c\u604d\u8c0e\u968d\u5fa8\u6e5f\u6f62\u9051\u749c\u8093\u7640\u87e5\u7bc1\u9cc7",
"hui": "\u7070\u6325\u8f89\u5fbd\u6062\u86d4\u56de\u6bc1\u6094\u6167\u5349\u60e0\u6666\u8d3f\u79fd\u4f1a\u70e9\u6c47\u8bb3\u8bf2\u7ed8\u8bd9\u8334\u835f\u8559\u54d5\u5599\u96b3\u6d04\u5f57\u7f0b\u73f2\u6656\u605a\u867a\u87ea\u9ebe",
"hun": "\u8364\u660f\u5a5a\u9b42\u6d51\u6df7\u8be8\u9984\u960d\u6eb7\u7f17",
"huo": "\u8c41\u6d3b\u4f19\u706b\u83b7\u6216\u60d1\u970d\u8d27\u7978\u6509\u56af\u5925\u94ac\u952a\u956c\u8020\u8816",
"ji": "\u51fb\u573e\u57fa\u673a\u7578\u7a3d\u79ef\u7b95\u808c\u9965\u8ff9\u6fc0\u8ba5\u9e21\u59ec\u7ee9\u7f09\u5409\u6781\u68d8\u8f91\u7c4d\u96c6\u53ca\u6025\u75be\u6c72\u5373\u5ac9\u7ea7\u6324\u51e0\u810a\u5df1\u84df\u6280\u5180\u5b63\u4f0e\u796d\u5242\u60b8\u6d4e\u5bc4\u5bc2\u8ba1\u8bb0\u65e2\u5fcc\u9645\u5993\u7ee7\u7eaa\u5c45\u4e0c\u4e69\u525e\u4f76\u4f74\u8114\u58bc\u82a8\u82b0\u8401\u84ba\u857a\u638e\u53fd\u54ad\u54dc\u5527\u5c8c\u5d74\u6d0e\u5f50\u5c50\u9aa5\u757f\u7391\u696b\u6b9b\u621f\u6222\u8d4d\u89ca\u7284\u9f51\u77f6\u7f81\u5d47\u7a37\u7620\u7635\u866e\u7b08\u7b04\u66a8\u8dfb\u8dfd\u9701\u9c9a\u9cab\u9afb\u9e82",
"jia": "\u5609\u67b7\u5939\u4f73\u5bb6\u52a0\u835a\u988a\u8d3e\u7532\u94be\u5047\u7a3c\u4ef7\u67b6\u9a7e\u5ac1\u4f3d\u90cf\u62ee\u5cac\u6d43\u8fe6\u73c8\u621b\u80db\u605d\u94d7\u9553\u75c2\u86f1\u7b33\u8888\u8dcf",
"jian": "\u6b7c\u76d1\u575a\u5c16\u7b3a\u95f4\u714e\u517c\u80a9\u8270\u5978\u7f04\u8327\u68c0\u67ec\u78b1\u7877\u62e3\u6361\u7b80\u4fed\u526a\u51cf\u8350\u69db\u9274\u8df5\u8d31\u89c1\u952e\u7bad\u4ef6\u5065\u8230\u5251\u996f\u6e10\u6e85\u6da7\u5efa\u50ed\u8c0f\u8c2b\u83c5\u84b9\u641b\u56dd\u6e54\u8e47\u8b07\u7f23\u67a7\u67d9\u6957\u620b\u622c\u726e\u728d\u6bfd\u8171\u7751\u950f\u9e63\u88e5\u7b15\u7bb4\u7fe6\u8dbc\u8e3a\u9ca3\u97af",
"jiang": "\u50f5\u59dc\u5c06\u6d46\u6c5f\u7586\u848b\u6868\u5956\u8bb2\u5320\u9171\u964d\u8333\u6d1a\u7edb\u7f30\u729f\u7913\u8029\u7ce8\u8c47",
"jiao": "\u8549\u6912\u7901\u7126\u80f6\u4ea4\u90ca\u6d47\u9a84\u5a07\u56bc\u6405\u94f0\u77eb\u4fa5\u811a\u72e1\u89d2\u997a\u7f34\u7ede\u527f\u6559\u9175\u8f7f\u8f83\u53eb\u4f7c\u50ec\u832d\u6322\u564d\u5ce4\u5fbc\u59e3\u7e9f\u656b\u768e\u9e6a\u86df\u91ae\u8de4\u9c9b",
"jie": "\u7a96\u63ed\u63a5\u7686\u79f8\u8857\u9636\u622a\u52ab\u8282\u6854\u6770\u6377\u776b\u7aed\u6d01\u7ed3\u89e3\u59d0\u6212\u85c9\u82a5\u754c\u501f\u4ecb\u75a5\u8beb\u5c4a\u5048\u8ba6\u8bd8\u5588\u55df\u736c\u5a55\u5b51\u6840\u7352\u78a3\u9534\u7596\u88b7\u9889\u86a7\u7faf\u9c92\u9ab1\u9aeb",
"jin": "\u5dfe\u7b4b\u65a4\u91d1\u4eca\u6d25\u895f\u7d27\u9526\u4ec5\u8c28\u8fdb\u9773\u664b\u7981\u8fd1\u70ec\u6d78\u5c3d\u537a\u8369\u5807\u5664\u9991\u5ed1\u5997\u7f19\u747e\u69ff\u8d46\u89d0\u9485\u9513\u887f\u77dc",
"jing": "\u52b2\u8346\u5162\u830e\u775b\u6676\u9cb8\u4eac\u60ca\u7cbe\u7cb3\u7ecf\u4e95\u8b66\u666f\u9888\u9759\u5883\u656c\u955c\u5f84\u75c9\u9756\u7adf\u7ade\u51c0\u522d\u5106\u9631\u83c1\u734d\u61ac\u6cfe\u8ff3\u5f2a\u5a67\u80bc\u80eb\u8148\u65cc",
"jiong": "\u70af\u7a98\u5182\u8fe5\u6243",
"jiu": "\u63ea\u7a76\u7ea0\u7396\u97ed\u4e45\u7078\u4e5d\u9152\u53a9\u6551\u65e7\u81fc\u8205\u548e\u5c31\u759a\u50e6\u557e\u9604\u67e9\u6855\u9e6b\u8d73\u9b0f",
"ju": "\u97a0\u62d8\u72d9\u75bd\u9a79\u83ca\u5c40\u5480\u77e9\u4e3e\u6cae\u805a\u62d2\u636e\u5de8\u5177\u8ddd\u8e1e\u952f\u4ff1\u53e5\u60e7\u70ac\u5267\u5028\u8bb5\u82e3\u82f4\u8392\u63ac\u907d\u5c66\u741a\u67b8\u6910\u6998\u6989\u6a58\u728b\u98d3\u949c\u9514\u7aad\u88fe\u8d84\u91b5\u8e3d\u9f83\u96ce\u97ab",
"juan": "\u6350\u9e43\u5a1f\u5026\u7737\u5377\u7ee2\u9104\u72f7\u6d93\u684a\u8832\u9529\u954c\u96bd",
"jue": "\u6485\u652b\u6289\u6398\u5014\u7235\u89c9\u51b3\u8bc0\u7edd\u53a5\u5282\u8c32\u77cd\u8568\u5658\u5d1b\u7357\u5b53\u73cf\u6877\u6a5b\u721d\u9562\u8e76\u89d6",
"jun": "\u5747\u83cc\u94a7\u519b\u541b\u5cfb\u4fca\u7ae3\u6d5a\u90e1\u9a8f\u6343\u72fb\u76b2\u7b60\u9e87",
"ka": "\u5580\u5496\u5361\u4f67\u5494\u80e9",
"ke": "\u54af\u5777\u82db\u67ef\u68f5\u78d5\u9897\u79d1\u58f3\u54b3\u53ef\u6e34\u514b\u523b\u5ba2\u8bfe\u5ca2\u606a\u6e98\u9a92\u7f02\u73c2\u8f72\u6c2a\u778c\u94b6\u75b4\u7aa0\u874c\u9ac1",
"kai": "\u5f00\u63e9\u6977\u51ef\u6168\u5240\u57b2\u8488\u5ffe\u607a\u94e0\u950e",
"kan": "\u520a\u582a\u52d8\u574e\u780d\u770b\u4f83\u51f5\u83b0\u83b6\u6221\u9f9b\u77b0",
"kang": "\u5eb7\u6177\u7ce0\u625b\u6297\u4ea2\u7095\u5751\u4f09\u95f6\u94aa",
"kao": "\u8003\u62f7\u70e4\u9760\u5c3b\u6832\u7292\u94d0",
"ken": "\u80af\u5543\u57a6\u6073\u57a0\u88c9\u9880",
"keng": "\u542d\u5fd0\u94ff",
"kong": "\u7a7a\u6050\u5b54\u63a7\u5025\u5d06\u7b9c",
"kou": "\u62a0\u53e3\u6263\u5bc7\u82a4\u853b\u53e9\u770d\u7b58",
"ku": "\u67af\u54ed\u7a9f\u82e6\u9177\u5e93\u88e4\u5233\u5800\u55be\u7ed4\u9ab7",
"kua": "\u5938\u57ae\u630e\u8de8\u80ef\u4f89",
"kuai": "\u5757\u7b77\u4fa9\u5feb\u84af\u90d0\u8489\u72ef\u810d",
"kuan": "\u5bbd\u6b3e\u9acb",
"kuang": "\u5321\u7b50\u72c2\u6846\u77ff\u7736\u65f7\u51b5\u8bd3\u8bf3\u909d\u5739\u593c\u54d0\u7ea9\u8d36",
"kui": "\u4e8f\u76d4\u5cbf\u7aa5\u8475\u594e\u9b41\u5080\u9988\u6127\u6e83\u9997\u532e\u5914\u9697\u63c6\u55b9\u559f\u609d\u6126\u9615\u9035\u668c\u777d\u8069\u8770\u7bd1\u81fe\u8dec",
"kun": "\u5764\u6606\u6346\u56f0\u6083\u9603\u7428\u951f\u918c\u9cb2\u9ae1",
"kuo": "\u62ec\u6269\u5ed3\u9614\u86de",
"la": "\u5783\u62c9\u5587\u8721\u814a\u8fa3\u5566\u524c\u647a\u908b\u65ef\u782c\u760c",
"lai": "\u83b1\u6765\u8d56\u5d03\u5f95\u6d9e\u6fd1\u8d49\u7750\u94fc\u765e\u7c41",
"lan": "\u84dd\u5a6a\u680f\u62e6\u7bee\u9611\u5170\u6f9c\u8c30\u63fd\u89c8\u61d2\u7f06\u70c2\u6ee5\u5549\u5c9a\u61d4\u6f24\u6984\u6593\u7f71\u9567\u8934",
"lang": "\u7405\u6994\u72fc\u5eca\u90ce\u6717\u6d6a\u83a8\u8497\u5577\u9606\u9512\u7a02\u8782",
"lao": "\u635e\u52b3\u7262\u8001\u4f6c\u59e5\u916a\u70d9\u6d9d\u5520\u5d02\u6833\u94d1\u94f9\u75e8\u91aa",
"le": "\u52d2\u4e50\u808b\u4ec2\u53fb\u561e\u6cd0\u9cd3",
"lei": "\u96f7\u956d\u857e\u78ca\u7d2f\u5121\u5792\u64c2\u7c7b\u6cea\u7fb8\u8bd4\u837d\u54a7\u6f2f\u5ad8\u7f27\u6a91\u8012\u9179",
"ling": "\u68f1\u51b7\u62ce\u73b2\u83f1\u96f6\u9f84\u94c3\u4f36\u7f9a\u51cc\u7075\u9675\u5cad\u9886\u53e6\u4ee4\u9143\u5844\u82d3\u5464\u56f9\u6ce0\u7eeb\u67c3\u68c2\u74f4\u8046\u86c9\u7fce\u9cae",
"leng": "\u695e\u6123",
"li": "\u5398\u68a8\u7281\u9ece\u7bf1\u72f8\u79bb\u6f13\u7406\u674e\u91cc\u9ca4\u793c\u8389\u8354\u540f\u6817\u4e3d\u5389\u52b1\u783e\u5386\u5229\u5088\u4f8b\u4fd0\u75e2\u7acb\u7c92\u6ca5\u96b6\u529b\u7483\u54e9\u4fea\u4fda\u90e6\u575c\u82c8\u8385\u84e0\u85dc\u6369\u5456\u5533\u55b1\u7301\u6ea7\u6fa7\u9026\u5a0c\u5ae0\u9a8a\u7f21\u73de\u67a5\u680e\u8f79\u623e\u783a\u8a48\u7f79\u9502\u9e42\u75a0\u75ac\u86ce\u870a\u8821\u7b20\u7be5\u7c9d\u91b4\u8dde\u96f3\u9ca1\u9ce2\u9ee7",
"lian": "\u4fe9\u8054\u83b2\u8fde\u9570\u5ec9\u601c\u6d9f\u5e18\u655b\u8138\u94fe\u604b\u70bc\u7ec3\u631b\u8539\u5941\u6f4b\u6fc2\u5a08\u740f\u695d\u6b93\u81c1\u81a6\u88e2\u880a\u9ca2",
"liang": "\u7cae\u51c9\u6881\u7cb1\u826f\u4e24\u8f86\u91cf\u667e\u4eae\u8c05\u589a\u690b\u8e09\u9753\u9b49",
"liao": "\u64a9\u804a\u50da\u7597\u71ce\u5be5\u8fbd\u6f66\u4e86\u6482\u9563\u5ed6\u6599\u84fc\u5c25\u5639\u7360\u5bee\u7f2d\u948c\u9e69\u8022",
"lie": "\u5217\u88c2\u70c8\u52a3\u730e\u51bd\u57d2\u6d0c\u8d94\u8e90\u9b23",
"lin": "\u7433\u6797\u78f7\u9716\u4e34\u90bb\u9cde\u6dcb\u51db\u8d41\u541d\u853a\u5d99\u5eea\u9074\u6aa9\u8f9a\u77b5\u7cbc\u8e8f\u9e9f",
"liu": "\u6e9c\u7409\u69b4\u786b\u998f\u7559\u5218\u7624\u6d41\u67f3\u516d\u62a1\u507b\u848c\u6cd6\u6d4f\u905b\u9a9d\u7efa\u65d2\u7198\u950d\u954f\u9e68\u938f",
"long": "\u9f99\u804b\u5499\u7b3c\u7abf\u9686\u5784\u62e2\u9647\u5f04\u5785\u830f\u6cf7\u73d1\u680a\u80e7\u783b\u7643",
"lou": "\u697c\u5a04\u6402\u7bd3\u6f0f\u964b\u55bd\u5d5d\u9542\u7618\u8027\u877c\u9ac5",
"lu": "\u82a6\u5362\u9885\u5e90\u7089\u63b3\u5364\u864f\u9c81\u9e93\u788c\u9732\u8def\u8d42\u9e7f\u6f5e\u7984\u5f55\u9646\u622e\u5786\u6445\u64b8\u565c\u6cf8\u6e0c\u6f09\u7490\u680c\u6a79\u8f73\u8f82\u8f98\u6c07\u80ea\u9565\u9e2c\u9e6d\u7c0f\u823b\u9c88",
"lv": "\u9a74\u5415\u94dd\u4fa3\u65c5\u5c65\u5c61\u7f15\u8651\u6c2f\u5f8b\u7387\u6ee4\u7eff\u634b\u95fe\u6988\u8182\u7a06\u891b",
"luan": "\u5ce6\u5b6a\u6ee6\u5375\u4e71\u683e\u9e3e\u92ae",
"lue": "\u63a0\u7565\u950a",
"lun": "\u8f6e\u4f26\u4ed1\u6ca6\u7eb6\u8bba\u56f5",
"luo": "\u841d\u87ba\u7f57\u903b\u9523\u7ba9\u9aa1\u88f8\u843d\u6d1b\u9a86\u7edc\u502e\u8366\u645e\u7321\u6cfa\u6924\u8136\u9559\u7630\u96d2",
"ma": "\u5988\u9ebb\u739b\u7801\u8682\u9a6c\u9a82\u561b\u5417\u551b\u72b8\u5b37\u6769\u9ebd",
"mai": "\u57cb\u4e70\u9ea6\u5356\u8fc8\u8109\u52a2\u836c\u54aa\u973e",
"man": "\u7792\u9992\u86ee\u6ee1\u8513\u66fc\u6162\u6f2b\u8c29\u5881\u5e54\u7f26\u71b3\u9558\u989f\u87a8\u9cd7\u9794",
"mang": "\u8292\u832b\u76f2\u5fd9\u83bd\u9099\u6f2d\u6726\u786d\u87d2",
"meng": "\u6c13\u840c\u8499\u6aac\u76df\u9530\u731b\u68a6\u5b5f\u52d0\u750d\u77a2\u61f5\u791e\u867b\u8722\u8813\u824b\u8268\u9efe",
"miao": "\u732b\u82d7\u63cf\u7784\u85d0\u79d2\u6e3a\u5e99\u5999\u55b5\u9088\u7f08\u7f2a\u676a\u6dfc\u7707\u9e4b\u8731",
"mao": "\u8305\u951a\u6bdb\u77db\u94c6\u536f\u8302\u5192\u5e3d\u8c8c\u8d38\u4f94\u88a4\u52d6\u8306\u5cc1\u7441\u6634\u7266\u8004\u65c4\u61cb\u7780\u86d1\u8765\u87ca\u9ae6",
"me": "\u4e48",
"mei": "\u73ab\u679a\u6885\u9176\u9709\u7164\u6ca1\u7709\u5a92\u9541\u6bcf\u7f8e\u6627\u5bd0\u59b9\u5a9a\u5776\u8393\u5d4b\u7338\u6d7c\u6e44\u6963\u9545\u9e5b\u8882\u9b45",
"men": "\u95e8\u95f7\u4eec\u626a\u739f\u7116\u61d1\u9494",
"mi": "\u772f\u919a\u9761\u7cdc\u8ff7\u8c1c\u5f25\u7c73\u79d8\u89c5\u6ccc\u871c\u5bc6\u5e42\u8288\u5196\u8c27\u863c\u5627\u7315\u736f\u6c68\u5b93\u5f2d\u8112\u6549\u7cf8\u7e3b\u9e8b",
"mian": "\u68c9\u7720\u7ef5\u5195\u514d\u52c9\u5a29\u7f05\u9762\u6c94\u6e4e\u817c\u7704",
"mie": "\u8511\u706d\u54a9\u881b\u7bfe",
"min": "\u6c11\u62bf\u76bf\u654f\u60af\u95fd\u82e0\u5cb7\u95f5\u6cef\u73c9",
"ming": "\u660e\u879f\u9e23\u94ed\u540d\u547d\u51a5\u8317\u6e9f\u669d\u7791\u9169",
"miu": "\u8c2c",
"mo": "\u6478\u6479\u8611\u6a21\u819c\u78e8\u6469\u9b54\u62b9\u672b\u83ab\u58a8\u9ed8\u6cab\u6f20\u5bde\u964c\u8c1f\u8309\u84e6\u998d\u5aeb\u9546\u79e3\u763c\u8031\u87c6\u8c8a\u8c98",
"mou": "\u8c0b\u725f\u67d0\u53b6\u54de\u5a7a\u7738\u936a",
"mu": "\u62c7\u7261\u4ea9\u59c6\u6bcd\u5893\u66ae\u5e55\u52df\u6155\u6728\u76ee\u7766\u7267\u7a46\u4eeb\u82dc\u5452\u6c90\u6bea\u94bc",
"na": "\u62ff\u54ea\u5450\u94a0\u90a3\u5a1c\u7eb3\u5185\u637a\u80ad\u954e\u8872\u7bac",
"nai": "\u6c16\u4e43\u5976\u8010\u5948\u9f10\u827f\u8418\u67f0",
"nan": "\u5357\u7537\u96be\u56ca\u5583\u56e1\u6960\u8169\u877b\u8d67",
"nao": "\u6320\u8111\u607c\u95f9\u5b6c\u57b4\u7331\u7459\u7847\u94d9\u86f2",
"ne": "\u6dd6\u5462\u8bb7",
"nei": "\u9981",
"nen": "\u5ae9\u80fd\u6798\u6041",
"ni": "\u59ae\u9713\u502a\u6ce5\u5c3c\u62df\u4f60\u533f\u817b\u9006\u6eba\u4f32\u576d\u730a\u6029\u6ee0\u6635\u65ce\u7962\u615d\u7768\u94cc\u9cb5",
"nian": "\u852b\u62c8\u5e74\u78be\u64b5\u637b\u5ff5\u5eff\u8f87\u9ecf\u9c87\u9cb6",
"niang": "\u5a18\u917f",
"niao": "\u9e1f\u5c3f\u8311\u5b32\u8132\u8885",
"nie": "\u634f\u8042\u5b7d\u556e\u954a\u954d\u6d85\u4e5c\u9667\u8616\u55eb\u8080\u989e\u81ec\u8e51",
"nin": "\u60a8\u67e0",
"ning": "\u72de\u51dd\u5b81\u62e7\u6cde\u4f5e\u84e5\u549b\u752f\u804d",
"niu": "\u725b\u626d\u94ae\u7ebd\u72c3\u5ff8\u599e\u86b4",
"nong": "\u8113\u6d53\u519c\u4fac",
"nu": "\u5974\u52aa\u6012\u5476\u5e11\u5f29\u80ec\u5b65\u9a7d",
"nv": "\u5973\u6067\u9495\u8844",
"nuan": "\u6696",
"nuenue": "\u8650",
"nue": "\u759f\u8c11",
"nuo": "\u632a\u61e6\u7cef\u8bfa\u50a9\u6426\u558f\u9518",
"ou": "\u54e6\u6b27\u9e25\u6bb4\u85d5\u5455\u5076\u6ca4\u6004\u74ef\u8026",
"pa": "\u556a\u8db4\u722c\u5e15\u6015\u7436\u8469\u7b62",
"pai": "\u62cd\u6392\u724c\u5f98\u6e43\u6d3e\u4ff3\u848e",
"pan": "\u6500\u6f58\u76d8\u78d0\u76fc\u7554\u5224\u53db\u723f\u6cee\u88a2\u897b\u87e0\u8e52",
"pang": "\u4e53\u5e9e\u65c1\u802a\u80d6\u6ec2\u9004",
"pao": "\u629b\u5486\u5228\u70ae\u888d\u8dd1\u6ce1\u530f\u72cd\u5e96\u812c\u75b1",
"pei": "\u5478\u80da\u57f9\u88f4\u8d54\u966a\u914d\u4f69\u6c9b\u638a\u8f94\u5e14\u6de0\u65c6\u952b\u9185\u9708",
"pen": "\u55b7\u76c6\u6e53",
"peng": "\u7830\u62a8\u70f9\u6f8e\u5f6d\u84ec\u68da\u787c\u7bf7\u81a8\u670b\u9e4f\u6367\u78b0\u576f\u580b\u562d\u6026\u87db",
"pi": "\u7812\u9739\u6279\u62ab\u5288\u7435\u6bd7\u5564\u813e\u75b2\u76ae\u5339\u75de\u50fb\u5c41\u8b6c\u4e15\u9674\u90b3\u90eb\u572e\u9f19\u64d7\u567c\u5e80\u5ab2\u7eb0\u6787\u7513\u7765\u7f74\u94cd\u75e6\u7656\u758b\u868d\u8c94",
"pian": "\u7bc7\u504f\u7247\u9a97\u8c1d\u9a88\u728f\u80fc\u890a\u7fe9\u8e41",
"piao": "\u98d8\u6f02\u74e2\u7968\u527d\u560c\u5ad6\u7f25\u6b8d\u779f\u87b5",
"pie": "\u6487\u77a5\u4e3f\u82e4\u6c15",
"pin": "\u62fc\u9891\u8d2b\u54c1\u8058\u62da\u59d8\u5ad4\u6980\u725d\u98a6",
"ping": "\u4e52\u576a\u82f9\u840d\u5e73\u51ed\u74f6\u8bc4\u5c4f\u4fdc\u5a09\u67b0\u9c86",
"po": "\u5761\u6cfc\u9887\u5a46\u7834\u9b44\u8feb\u7c95\u53f5\u9131\u6ea5\u73c0\u948b\u94b7\u76a4\u7b38",
"pou": "\u5256\u88d2\u8e23",
"pu": "\u6251\u94fa\u4ec6\u8386\u8461\u83e9\u84b2\u57d4\u6734\u5703\u666e\u6d66\u8c31\u66dd\u7011\u530d\u5657\u6fee\u749e\u6c06\u9564\u9568\u8e7c",
"qi": "\u671f\u6b3a\u6816\u621a\u59bb\u4e03\u51c4\u6f06\u67d2\u6c8f\u5176\u68cb\u5947\u6b67\u7566\u5d0e\u8110\u9f50\u65d7\u7948\u7941\u9a91\u8d77\u5c82\u4e5e\u4f01\u542f\u5951\u780c\u5668\u6c14\u8fc4\u5f03\u6c7d\u6ce3\u8bab\u4e9f\u4e93\u573b\u8291\u840b\u847a\u5601\u5c7a\u5c90\u6c54\u6dc7\u9a90\u7eee\u742a\u7426\u675e\u6864\u69ed\u6b39\u797a\u61a9\u789b\u86f4\u871e\u7da6\u7dae\u8dbf\u8e4a\u9ccd\u9e92",
"qia": "\u6390\u6070\u6d3d\u845c",
"qian": "\u7275\u6266\u948e\u94c5\u5343\u8fc1\u7b7e\u4edf\u8c26\u4e7e\u9ed4\u94b1\u94b3\u524d\u6f5c\u9063\u6d45\u8c34\u5811\u5d4c\u6b20\u6b49\u4f65\u9621\u828a\u82a1\u8368\u63ae\u5c8d\u60ad\u614a\u9a9e\u6434\u8930\u7f31\u6920\u80b7\u6106\u94a4\u8654\u7b9d",
"qiang": "\u67aa\u545b\u8154\u7f8c\u5899\u8537\u5f3a\u62a2\u5af1\u6a2f\u6217\u709d\u9516\u9535\u956a\u8941\u8723\u7f9f\u8deb\u8dc4",
"qiao": "\u6a47\u9539\u6572\u6084\u6865\u77a7\u4e54\u4fa8\u5de7\u9798\u64ac\u7fd8\u5ced\u4fcf\u7a8d\u5281\u8bee\u8c2f\u835e\u6100\u6194\u7f32\u6a35\u6bf3\u7857\u8df7\u9792",
"qie": "\u5207\u8304\u4e14\u602f\u7a83\u90c4\u553c\u60ec\u59be\u6308\u9532\u7ba7",
"qin": "\u94a6\u4fb5\u4eb2\u79e6\u7434\u52e4\u82b9\u64d2\u79bd\u5bdd\u6c81\u82a9\u84c1\u8572\u63ff\u5423\u55ea\u5659\u6eb1\u6a8e\u8793\u887e",
"qing": "\u9752\u8f7b\u6c22\u503e\u537f\u6e05\u64ce\u6674\u6c30\u60c5\u9877\u8bf7\u5e86\u5029\u82d8\u570a\u6aa0\u78ec\u873b\u7f44\u7b90\u8b26\u9cad\u9ee5",
"qiong": "\u743c\u7a77\u909b\u8315\u7a79\u7b47\u928e",
"qiu": "\u79cb\u4e18\u90b1\u7403\u6c42\u56da\u914b\u6cc5\u4fc5\u6c3d\u5def\u827d\u72b0\u6e6b\u9011\u9052\u6978\u8d47\u9e20\u866c\u86af\u8764\u88d8\u7cd7\u9cc5\u9f3d",
"qu": "\u8d8b\u533a\u86c6\u66f2\u8eaf\u5c48\u9a71\u6e20\u53d6\u5a36\u9f8b\u8da3\u53bb\u8bce\u52ac\u8556\u8627\u5c96\u8862\u9612\u74a9\u89d1\u6c0d\u795b\u78f2\u766f\u86d0\u883c\u9eb4\u77bf\u9ee2",
"quan": "\u5708\u98a7\u6743\u919b\u6cc9\u5168\u75ca\u62f3\u72ac\u5238\u529d\u8be0\u8343\u737e\u609b\u7efb\u8f81\u754e\u94e8\u8737\u7b4c\u9b08",
"que": "\u7f3a\u7094\u7638\u5374\u9e4a\u69b7\u786e\u96c0\u9619\u60ab",
"qun": "\u88d9\u7fa4\u9021",
"ran": "\u7136\u71c3\u5189\u67d3\u82d2\u9aef",
"rang": "\u74e4\u58e4\u6518\u56b7\u8ba9\u79b3\u7a70",
"rao": "\u9976\u6270\u7ed5\u835b\u5a06\u6861",
"ruo": "\u60f9\u82e5\u5f31",
"re": "\u70ed\u504c",
"ren": "\u58ec\u4ec1\u4eba\u5fcd\u97e7\u4efb\u8ba4\u5203\u598a\u7eab\u4ede\u834f\u845a\u996a\u8f6b\u7a14\u887d",
"reng": "\u6254\u4ecd",
"ri": "\u65e5",
"rong": "\u620e\u8338\u84c9\u8363\u878d\u7194\u6eb6\u5bb9\u7ed2\u5197\u5d58\u72e8\u7f1b\u6995\u877e",
"rou": "\u63c9\u67d4\u8089\u7cc5\u8e42\u97a3",
"ru": "\u8339\u8815\u5112\u5b7a\u5982\u8fb1\u4e73\u6c5d\u5165\u8925\u84d0\u85b7\u5685\u6d33\u6ebd\u6fe1\u94f7\u8966\u98a5",
"ruan": "\u8f6f\u962e\u670a",
"rui": "\u854a\u745e\u9510\u82ae\u8564\u777f\u868b",
"run": "\u95f0\u6da6",
"sa": "\u6492\u6d12\u8428\u5345\u4ee8\u6332\u98d2",
"sai": "\u816e\u9cc3\u585e\u8d5b\u567b",
"san": "\u4e09\u53c1\u4f1e\u6563\u5f61\u9993\u6c35\u6bf5\u7cc1\u9730",
"sang": "\u6851\u55d3\u4e27\u6421\u78c9\u98a1",
"sao": "\u6414\u9a9a\u626b\u5ac2\u57fd\u81ca\u7619\u9ccb",
"se": "\u745f\u8272\u6da9\u556c\u94e9\u94ef\u7a51",
"sen": "\u68ee",
"seng": "\u50e7",
"sha": "\u838e\u7802\u6740\u5239\u6c99\u7eb1\u50bb\u5565\u715e\u810e\u6b43\u75e7\u88df\u970e\u9ca8",
"shai": "\u7b5b\u6652\u917e",
"shan": "\u73ca\u82eb\u6749\u5c71\u5220\u717d\u886b\u95ea\u9655\u64c5\u8d61\u81b3\u5584\u6c55\u6247\u7f2e\u5261\u8baa\u912f\u57cf\u829f\u6f78\u59d7\u9a9f\u81bb\u9490\u759d\u87ee\u8222\u8dda\u9cdd",
"shang": "\u5892\u4f24\u5546\u8d4f\u664c\u4e0a\u5c1a\u88f3\u57a7\u7ef1\u6b87\u71b5\u89de",
"shao": "\u68a2\u634e\u7a0d\u70e7\u828d\u52fa\u97f6\u5c11\u54e8\u90b5\u7ecd\u52ad\u82d5\u6f72\u86f8\u7b24\u7b72\u8244",
"she": "\u5962\u8d4a\u86c7\u820c\u820d\u8d66\u6444\u5c04\u6151\u6d89\u793e\u8bbe\u538d\u4f58\u731e\u7572\u9e9d",
"shen": "\u7837\u7533\u547b\u4f38\u8eab\u6df1\u5a20\u7ec5\u795e\u6c88\u5ba1\u5a76\u751a\u80be\u614e\u6e17\u8bdc\u8c02\u5432\u54c2\u6e16\u6939\u77e7\u8703",
"sheng": "\u58f0\u751f\u7525\u7272\u5347\u7ef3\u7701\u76db\u5269\u80dc\u5723\u4e1e\u6e11\u5ab5\u771a\u7b19",
"shi": "\u5e08\u5931\u72ee\u65bd\u6e7f\u8bd7\u5c38\u8671\u5341\u77f3\u62fe\u65f6\u4ec0\u98df\u8680\u5b9e\u8bc6\u53f2\u77e2\u4f7f\u5c4e\u9a76\u59cb\u5f0f\u793a\u58eb\u4e16\u67ff\u4e8b\u62ed\u8a93\u901d\u52bf\u662f\u55dc\u566c\u9002\u4ed5\u4f8d\u91ca\u9970\u6c0f\u5e02\u6043\u5ba4\u89c6\u8bd5\u8c25\u57d8\u83b3\u84cd\u5f11\u5511\u9963\u8f7c\u8006\u8d33\u70bb\u793b\u94c8\u94ca\u87ab\u8210\u7b6e\u8c55\u9ca5\u9cba",
"shou": "\u6536\u624b\u9996\u5b88\u5bff\u6388\u552e\u53d7\u7626\u517d\u624c\u72e9\u7ef6\u824f",
"shu": "\u852c\u67a2\u68b3\u6b8a\u6292\u8f93\u53d4\u8212\u6dd1\u758f\u4e66\u8d4e\u5b70\u719f\u85af\u6691\u66d9\u7f72\u8700\u9ecd\u9f20\u5c5e\u672f\u8ff0\u6811\u675f\u620d\u7ad6\u5885\u5eb6\u6570\u6f31\u6055\u500f\u587e\u83fd\u5fc4\u6cad\u6d91\u6f8d\u59dd\u7ebe\u6bf9\u8167\u6bb3\u956f\u79eb\u9e6c",
"shua": "\u5237\u800d\u5530\u6dae",
"shuai": "\u6454\u8870\u7529\u5e05\u87c0",
"shuan": "\u6813\u62f4\u95e9",
"shuang": "\u971c\u53cc\u723d\u5b40",
"shui": "\u8c01\u6c34\u7761\u7a0e",
"shun": "\u542e\u77ac\u987a\u821c\u6042",
"shuo": "\u8bf4\u7855\u6714\u70c1\u84b4\u6420\u55cd\u6fef\u5981\u69ca\u94c4",
"si": "\u65af\u6495\u5636\u601d\u79c1\u53f8\u4e1d\u6b7b\u8086\u5bfa\u55e3\u56db\u4f3a\u4f3c\u9972\u5df3\u53ae\u4fdf\u5155\u83e5\u549d\u6c5c\u6cd7\u6f8c\u59d2\u9a77\u7f0c\u7940\u7960\u9536\u9e36\u801c\u86f3\u7b25",
"song": "\u677e\u8038\u6002\u9882\u9001\u5b8b\u8bbc\u8bf5\u51c7\u83d8\u5d27\u5d69\u5fea\u609a\u6dde\u7ae6",
"sou": "\u641c\u8258\u64de\u55fd\u53df\u55d6\u55fe\u998a\u6eb2\u98d5\u778d\u953c\u878b",
"su": "\u82cf\u9165\u4fd7\u7d20\u901f\u7c9f\u50f3\u5851\u6eaf\u5bbf\u8bc9\u8083\u5919\u8c21\u850c\u55c9\u612b\u7c0c\u89eb\u7a23",
"suan": "\u9178\u849c\u7b97",
"sui": "\u867d\u968b\u968f\u7ee5\u9ad3\u788e\u5c81\u7a57\u9042\u96a7\u795f\u84d1\u51ab\u8c07\u6fc9\u9083\u71e7\u772d\u7762",
"sun": "\u5b59\u635f\u7b0b\u836a\u72f2\u98e7\u69ab\u8de3\u96bc",
"suo": "\u68ad\u5506\u7f29\u7410\u7d22\u9501\u6240\u5522\u55e6\u5a11\u686b\u7743\u7fa7",
"ta": "\u584c\u4ed6\u5b83\u5979\u5854\u736d\u631e\u8e4b\u8e0f\u95fc\u6ebb\u9062\u69bb\u6c93",
"tai": "\u80ce\u82d4\u62ac\u53f0\u6cf0\u915e\u592a\u6001\u6c70\u90b0\u85b9\u80bd\u70b1\u949b\u8dc6\u9c90",
"tan": "\u574d\u644a\u8d2a\u762b\u6ee9\u575b\u6a80\u75f0\u6f6d\u8c2d\u8c08\u5766\u6bef\u8892\u78b3\u63a2\u53f9\u70ad\u90ef\u8548\u6619\u94bd\u952c\u8983",
"tang": "\u6c64\u5858\u642a\u5802\u68e0\u819b\u5510\u7cd6\u50a5\u9967\u6e8f\u746d\u94f4\u9557\u8025\u8797\u87b3\u7fb0\u91a3",
"thang": "\u5018\u8eba\u6dcc",
"theng": "\u8d9f\u70eb",
"tao": "\u638f\u6d9b\u6ed4\u7ee6\u8404\u6843\u9003\u6dd8\u9676\u8ba8\u5957\u6311\u9f17\u5555\u97ec\u9955",
"te": "\u7279",
"teng": "\u85e4\u817e\u75bc\u8a8a\u6ed5",
"ti": "\u68af\u5254\u8e22\u9511\u63d0\u9898\u8e44\u557c\u4f53\u66ff\u568f\u60d5\u6d95\u5243\u5c49\u8351\u608c\u9016\u7ee8\u7f07\u9e48\u88fc\u918d",
"tian": "\u5929\u6dfb\u586b\u7530\u751c\u606c\u8214\u8146\u63ad\u5fdd\u9617\u6b84\u754b\u94bf\u86ba",
"tiao": "\u6761\u8fe2\u773a\u8df3\u4f7b\u7967\u94eb\u7a95\u9f86\u9ca6",
"tie": "\u8d34\u94c1\u5e16\u841c\u992e",
"ting": "\u5385\u542c\u70c3\u6c40\u5ef7\u505c\u4ead\u5ead\u633a\u8247\u839b\u8476\u5a77\u6883\u8713\u9706",
"tong": "\u901a\u6850\u916e\u77b3\u540c\u94dc\u5f64\u7ae5\u6876\u6345\u7b52\u7edf\u75db\u4f5f\u50ee\u4edd\u833c\u55f5\u6078\u6f7c\u783c",
"tou": "\u5077\u6295\u5934\u900f\u4ea0",
"tu": "\u51f8\u79c3\u7a81\u56fe\u5f92\u9014\u6d82\u5c60\u571f\u5410\u5154\u580d\u837c\u83df\u948d\u9174",
"tuan": "\u6e4d\u56e2\u7583",
"tui": "\u63a8\u9893\u817f\u8715\u892a\u9000\u5fd2\u717a",
"tun": "\u541e\u5c6f\u81c0\u9968\u66be\u8c5a\u7a80",
"tuo": "\u62d6\u6258\u8131\u9e35\u9640\u9a6e\u9a7c\u692d\u59a5\u62d3\u553e\u4e47\u4f57\u5768\u5eb9\u6cb1\u67dd\u7823\u7ba8\u8204\u8dce\u9f0d",
"wa": "\u6316\u54c7\u86d9\u6d3c\u5a03\u74e6\u889c\u4f64\u5a32\u817d",
"wai": "\u6b6a\u5916",
"wan": "\u8c4c\u5f2f\u6e7e\u73a9\u987d\u4e38\u70f7\u5b8c\u7897\u633d\u665a\u7696\u60cb\u5b9b\u5a49\u4e07\u8155\u525c\u8284\u82cb\u83c0\u7ea8\u7efe\u742c\u8118\u7579\u873f\u7ba2",
"wang": "\u6c6a\u738b\u4ea1\u6789\u7f51\u5f80\u65fa\u671b\u5fd8\u5984\u7f54\u5c22\u60d8\u8f8b\u9b4d",
"wei": "\u5a01\u5dcd\u5fae\u5371\u97e6\u8fdd\u6845\u56f4\u552f\u60df\u4e3a\u6f4d\u7ef4\u82c7\u840e\u59d4\u4f1f\u4f2a\u5c3e\u7eac\u672a\u851a\u5473\u754f\u80c3\u5582\u9b4f\u4f4d\u6e2d\u8c13\u5c09\u6170\u536b\u502d\u504e\u8bff\u9688\u8473\u8587\u5e0f\u5e37\u5d34\u5d6c\u7325\u732c\u95f1\u6ca9\u6d27\u6da0\u9036\u5a13\u73ae\u97ea\u8ece\u709c\u7168\u71a8\u75ff\u8249\u9c94",
"wen": "\u761f\u6e29\u868a\u6587\u95fb\u7eb9\u543b\u7a33\u7d0a\u95ee\u520e\u6120\u960c\u6c76\u74ba\u97eb\u6b81\u96ef",
"weng": "\u55e1\u7fc1\u74ee\u84ca\u8579",
"wo": "\u631d\u8717\u6da1\u7a9d\u6211\u65a1\u5367\u63e1\u6c83\u83b4\u5e44\u6e25\u674c\u809f\u9f8c",
"wu": "\u5deb\u545c\u94a8\u4e4c\u6c61\u8bec\u5c4b\u65e0\u829c\u68a7\u543e\u5434\u6bcb\u6b66\u4e94\u6342\u5348\u821e\u4f0d\u4fae\u575e\u620a\u96fe\u6664\u7269\u52ff\u52a1\u609f\u8bef\u5140\u4ef5\u9622\u90ac\u572c\u82b4\u5e91\u6003\u5fe4\u6d6f\u5be4\u8fd5\u59a9\u9a9b\u727e\u7110\u9e49\u9e5c\u8708\u92c8\u9f2f",
"xi": "\u6614\u7199\u6790\u897f\u7852\u77fd\u6670\u563b\u5438\u9521\u727a\u7a00\u606f\u5e0c\u6089\u819d\u5915\u60dc\u7184\u70ef\u6eaa\u6c50\u7280\u6a84\u88ad\u5e2d\u4e60\u5ab3\u559c\u94e3\u6d17\u7cfb\u9699\u620f\u7ec6\u50d6\u516e\u96b0\u90d7\u831c\u8478\u84f0\u595a\u550f\u5f99\u9969\u960b\u6d60\u6dc5\u5c63\u5b09\u73ba\u6a28\u66e6\u89cb\u6b37\u71b9\u798a\u79a7\u94b8\u7699\u7a78\u8725\u87cb\u823e\u7fb2\u7c9e\u7fd5\u91af\u9f37",
"xia": "\u778e\u867e\u5323\u971e\u8f96\u6687\u5ce1\u4fa0\u72ed\u4e0b\u53a6\u590f\u5413\u6380\u846d\u55c4\u72ce\u9050\u7455\u7856\u7615\u7f45\u9ee0",
"xian": "\u9528\u5148\u4ed9\u9c9c\u7ea4\u54b8\u8d24\u8854\u8237\u95f2\u6d8e\u5f26\u5acc\u663e\u9669\u73b0\u732e\u53bf\u817a\u9985\u7fa1\u5baa\u9677\u9650\u7ebf\u51bc\u85d3\u5c98\u7303\u66b9\u5a34\u6c19\u7946\u9e47\u75eb\u86ac\u7b45\u7c7c\u9170\u8df9",
"xiang": "\u76f8\u53a2\u9576\u9999\u7bb1\u8944\u6e58\u4e61\u7fd4\u7965\u8be6\u60f3\u54cd\u4eab\u9879\u5df7\u6a61\u50cf\u5411\u8c61\u8297\u8459\u9977\u5ea0\u9aa7\u7f03\u87d3\u9c9e\u98e8",
"xiao": "\u8427\u785d\u9704\u524a\u54ee\u56a3\u9500\u6d88\u5bb5\u6dc6\u6653\u5c0f\u5b5d\u6821\u8096\u5578\u7b11\u6548\u54d3\u54bb\u5d24\u6f47\u900d\u9a81\u7ee1\u67ad\u67b5\u7b71\u7bab\u9b48",
"xie": "\u6954\u4e9b\u6b47\u874e\u978b\u534f\u631f\u643a\u90aa\u659c\u80c1\u8c10\u5199\u68b0\u5378\u87f9\u61c8\u6cc4\u6cfb\u8c22\u5c51\u5055\u4eb5\u52f0\u71ee\u85a4\u64b7\u5ee8\u7023\u9082\u7ec1\u7f2c\u69ad\u698d\u6b59\u8e9e",
"xin": "\u85aa\u82af\u950c\u6b23\u8f9b\u65b0\u5ffb\u5fc3\u4fe1\u8845\u56df\u99a8\u8398\u6b46\u94fd\u946b",
"xing": "\u661f\u8165\u7329\u60fa\u5174\u5211\u578b\u5f62\u90a2\u884c\u9192\u5e78\u674f\u6027\u59d3\u9649\u8347\u8365\u64e4\u60bb\u784e",
"xiong": "\u5144\u51f6\u80f8\u5308\u6c79\u96c4\u718a\u828e",
"xiu": "\u4f11\u4fee\u7f9e\u673d\u55c5\u9508\u79c0\u8896\u7ee3\u83a0\u5cab\u9990\u5ea5\u9e3a\u8c85\u9af9",
"xu": "\u589f\u620c\u9700\u865a\u5618\u987b\u5f90\u8bb8\u84c4\u9157\u53d9\u65ed\u5e8f\u755c\u6064\u7d6e\u5a7f\u7eea\u7eed\u8bb4\u8be9\u5729\u84ff\u6035\u6d2b\u6e86\u987c\u6829\u7166\u7809\u76f1\u80e5\u7cc8\u9191",
"xuan": "\u8f69\u55a7\u5ba3\u60ac\u65cb\u7384\u9009\u7663\u7729\u7eda\u5107\u8c16\u8431\u63ce\u9994\u6ceb\u6d35\u6e32\u6f29\u7487\u6966\u6684\u70ab\u714a\u78b9\u94c9\u955f\u75c3",
"xue": "\u9774\u859b\u5b66\u7a74\u96ea\u8840\u5671\u6cf6\u9cd5",
"xun": "\u52cb\u718f\u5faa\u65ec\u8be2\u5bfb\u9a6f\u5de1\u6b89\u6c5b\u8bad\u8baf\u900a\u8fc5\u5dfd\u57d9\u8340\u85b0\u5ccb\u5f87\u6d54\u66db\u7aa8\u91ba\u9c9f",
"ya": "\u538b\u62bc\u9e26\u9e2d\u5440\u4e2b\u82bd\u7259\u869c\u5d16\u8859\u6daf\u96c5\u54d1\u4e9a\u8bb6\u4f22\u63e0\u5416\u5c88\u8fd3\u5a05\u740a\u6860\u6c29\u7811\u775a\u75d6",
"yan": "\u7109\u54bd\u9609\u70df\u6df9\u76d0\u4e25\u7814\u8712\u5ca9\u5ef6\u8a00\u989c\u960e\u708e\u6cbf\u5944\u63a9\u773c\u884d\u6f14\u8273\u5830\u71d5\u538c\u781a\u96c1\u5501\u5f66\u7130\u5bb4\u8c1a\u9a8c\u53a3\u9765\u8d5d\u4fe8\u5043\u5156\u8ba0\u8c33\u90fe\u9122\u82ab\u83f8\u5d26\u6079\u95eb\u960f\u6d07\u6e6e\u6edf\u598d\u5ae3\u7430\u664f\u80ed\u814c\u7131\u7f68\u7b75\u917d\u9b47\u990d\u9f39",
"yang": "\u6b83\u592e\u9e2f\u79e7\u6768\u626c\u4f6f\u75a1\u7f8a\u6d0b\u9633\u6c27\u4ef0\u75d2\u517b\u6837\u6f3e\u5f89\u600f\u6cf1\u7080\u70ca\u6059\u86d8\u9785",
"yao": "\u9080\u8170\u5996\u7476\u6447\u5c27\u9065\u7a91\u8c23\u59da\u54ac\u8200\u836f\u8981\u8000\u592d\u723b\u5406\u5d3e\u5fad\u7039\u5e7a\u73e7\u6773\u66dc\u80b4\u9e5e\u7a88\u7e47\u9cd0",
"ye": "\u6930\u564e\u8036\u7237\u91ce\u51b6\u4e5f\u9875\u6396\u4e1a\u53f6\u66f3\u814b\u591c\u6db2\u8c12\u90ba\u63f6\u9980\u6654\u70e8\u94d8",
"yi": "\u4e00\u58f9\u533b\u63d6\u94f1\u4f9d\u4f0a\u8863\u9890\u5937\u9057\u79fb\u4eea\u80f0\u7591\u6c82\u5b9c\u59e8\u5f5d\u6905\u8681\u501a\u5df2\u4e59\u77e3\u4ee5\u827a\u6291\u6613\u9091\u5c79\u4ebf\u5f79\u81c6\u9038\u8084\u75ab\u4ea6\u88d4\u610f\u6bc5\u5fc6\u4e49\u76ca\u6ea2\u8be3\u8bae\u8c0a\u8bd1\u5f02\u7ffc\u7fcc\u7ece\u5208\u5293\u4f7e\u8bd2\u572a\u572f\u57f8\u61ff\u82e1\u858f\u5f08\u5955\u6339\u5f0b\u5453\u54a6\u54bf\u566b\u5cc4\u5db7\u7317\u9974\u603f\u6021\u6092\u6f2a\u8fe4\u9a7f\u7f22\u6baa\u8d3b\u65d6\u71a0\u9487\u9552\u9571\u75cd\u7617\u7654\u7fca\u8864\u8734\u8223\u7fbf\u7ff3\u914f\u9edf",
"yin": "\u8335\u836b\u56e0\u6bb7\u97f3\u9634\u59fb\u541f\u94f6\u6deb\u5bc5\u996e\u5c39\u5f15\u9690\u5370\u80e4\u911e\u5819\u831a\u5591\u72fa\u5924\u6c24\u94df\u763e\u8693\u972a\u9f88",
"ying": "\u82f1\u6a31\u5a74\u9e70\u5e94\u7f28\u83b9\u8424\u8425\u8367\u8747\u8fce\u8d62\u76c8\u5f71\u9896\u786c\u6620\u5b34\u90e2\u8314\u83ba\u8426\u6484\u5624\u81ba\u6ee2\u6f46\u701b\u745b\u748e\u6979\u9e66\u763f\u988d\u7f42",
"yo": "\u54df\u5537",
"yong": "\u62e5\u4f63\u81c3\u75c8\u5eb8\u96cd\u8e0a\u86f9\u548f\u6cf3\u6d8c\u6c38\u607f\u52c7\u7528\u4fd1\u58c5\u5889\u6175\u9095\u955b\u752c\u9cd9\u9954",
"you": "\u5e7d\u4f18\u60a0\u5fe7\u5c24\u7531\u90ae\u94c0\u72b9\u6cb9\u6e38\u9149\u6709\u53cb\u53f3\u4f51\u91c9\u8bf1\u53c8\u5e7c\u5363\u6538\u4f91\u83b8\u5466\u56ff\u5ba5\u67da\u7337\u7256\u94d5\u75a3\u8763\u9c7f\u9edd\u9f2c",
"yu": "\u8fc2\u6de4\u4e8e\u76c2\u6986\u865e\u611a\u8206\u4f59\u4fde\u903e\u9c7c\u6109\u6e1d\u6e14\u9685\u4e88\u5a31\u96e8\u4e0e\u5c7f\u79b9\u5b87\u8bed\u7fbd\u7389\u57df\u828b\u90c1\u5401\u9047\u55bb\u5cea\u5fa1\u6108\u6b32\u72f1\u80b2\u8a89\u6d74\u5bd3\u88d5\u9884\u8c6b\u9a6d\u79ba\u6bd3\u4f1b\u4fe3\u8c00\u8c15\u8438\u84e3\u63c4\u5581\u5704\u5709\u5d5b\u72f3\u996b\u5ebe\u9608\u59aa\u59a4\u7ea1\u745c\u6631\u89ce\u8174\u6b24\u65bc\u715c\u71e0\u807f\u94b0\u9e46\u7610\u7600\u7ab3\u8753\u7afd\u8201\u96e9\u9f89",
"yuan": "\u9e33\u6e0a\u51a4\u5143\u57a3\u8881\u539f\u63f4\u8f95\u56ed\u5458\u5706\u733f\u6e90\u7f18\u8fdc\u82d1\u613f\u6028\u9662\u586c\u6c85\u5a9b\u7457\u6a7c\u7230\u7722\u9e22\u8788\u9f0b",
"yue": "\u66f0\u7ea6\u8d8a\u8dc3\u94a5\u5cb3\u7ca4\u6708\u60a6\u9605\u9fa0\u6a3e\u5216\u94ba",
"yun": "\u8018\u4e91\u90e7\u5300\u9668\u5141\u8fd0\u8574\u915d\u6655\u97f5\u5b55\u90d3\u82b8\u72c1\u607d\u7ead\u6b92\u6600\u6c32",
"za": "\u531d\u7838\u6742\u62f6\u5482",
"zai": "\u683d\u54c9\u707e\u5bb0\u8f7d\u518d\u5728\u54b1\u5d3d\u753e",
"zan": "\u6512\u6682\u8d5e\u74d2\u661d\u7c2a\u7ccc\u8db1\u933e",
"zang": "\u8d43\u810f\u846c\u5958\u6215\u81e7",
"zao": "\u906d\u7cdf\u51ff\u85fb\u67a3\u65e9\u6fa1\u86a4\u8e81\u566a\u9020\u7682\u7076\u71e5\u5523\u7f2b",
"ze": "\u8d23\u62e9\u5219\u6cfd\u4ec4\u8d5c\u5567\u8fee\u6603\u7b2e\u7ba6\u8234",
"zei": "\u8d3c",
"zen": "\u600e\u8c2e",
"zeng": "\u589e\u618e\u66fe\u8d60\u7f2f\u7511\u7f7e\u9503",
"zha": "\u624e\u55b3\u6e23\u672d\u8f67\u94e1\u95f8\u7728\u6805\u69a8\u548b\u4e4d\u70b8\u8bc8\u63f8\u5412\u54a4\u54f3\u600d\u781f\u75c4\u86b1\u9f44",
"zhai": "\u6458\u658b\u5b85\u7a84\u503a\u5be8\u7826",
"zhan": "\u77bb\u6be1\u8a79\u7c98\u6cbe\u76cf\u65a9\u8f97\u5d2d\u5c55\u8638\u6808\u5360\u6218\u7ad9\u6e5b\u7efd\u8c35\u640c\u65c3",
"zhang": "\u6a1f\u7ae0\u5f70\u6f33\u5f20\u638c\u6da8\u6756\u4e08\u5e10\u8d26\u4ed7\u80c0\u7634\u969c\u4ec9\u9123\u5e5b\u5d82\u7350\u5adc\u748b\u87d1",
"zhao": "\u62db\u662d\u627e\u6cbc\u8d75\u7167\u7f69\u5146\u8087\u53ec\u722a\u8bcf\u68f9\u948a\u7b0a",
"zhe": "\u906e\u6298\u54f2\u86f0\u8f99\u8005\u9517\u8517\u8fd9\u6d59\u8c2a\u966c\u67d8\u8f84\u78d4\u9e67\u891a\u8707\u8d6d",
"zhen": "\u73cd\u659f\u771f\u7504\u7827\u81fb\u8d1e\u9488\u4fa6\u6795\u75b9\u8bca\u9707\u632f\u9547\u9635\u7f1c\u6862\u699b\u8f78\u8d48\u80d7\u6715\u796f\u755b\u9e29",
"zheng": "\u84b8\u6323\u7741\u5f81\u72f0\u4e89\u6014\u6574\u62ef\u6b63\u653f\u5e27\u75c7\u90d1\u8bc1\u8be4\u5ce5\u94b2\u94ee\u7b5d",
"zhi": "\u829d\u679d\u652f\u5431\u8718\u77e5\u80a2\u8102\u6c41\u4e4b\u7ec7\u804c\u76f4\u690d\u6b96\u6267\u503c\u4f84\u5740\u6307\u6b62\u8dbe\u53ea\u65e8\u7eb8\u5fd7\u631a\u63b7\u81f3\u81f4\u7f6e\u5e1c\u5cd9\u5236\u667a\u79e9\u7a1a\u8d28\u7099\u75d4\u6ede\u6cbb\u7a92\u536e\u965f\u90c5\u57f4\u82b7\u646d\u5e19\u5fee\u5f58\u54ab\u9a98\u6809\u67b3\u6800\u684e\u8f75\u8f7e\u6534\u8d3d\u81a3\u7949\u7957\u9ef9\u96c9\u9e37\u75e3\u86ed\u7d77\u916f\u8dd6\u8e2c\u8e2f\u8c78\u89ef",
"zhong": "\u4e2d\u76c5\u5fe0\u949f\u8877\u7ec8\u79cd\u80bf\u91cd\u4ef2\u4f17\u51a2\u953a\u87bd\u8202\u822f\u8e35",
"zhou": "\u821f\u5468\u5dde\u6d32\u8bcc\u7ca5\u8f74\u8098\u5e1a\u5492\u76b1\u5b99\u663c\u9aa4\u5544\u7740\u501c\u8bf9\u836e\u9b3b\u7ea3\u80c4\u78a1\u7c40\u8233\u914e\u9cb7",
"zhu": "\u73e0\u682a\u86db\u6731\u732a\u8bf8\u8bdb\u9010\u7af9\u70db\u716e\u62c4\u77a9\u5631\u4e3b\u8457\u67f1\u52a9\u86c0\u8d2e\u94f8\u7b51\u4f4f\u6ce8\u795d\u9a7b\u4f2b\u4f8f\u90be\u82ce\u8331\u6d19\u6e1a\u6f74\u9a7a\u677c\u69e0\u6a65\u70b7\u94e2\u75b0\u7603\u86b0\u7afa\u7bb8\u7fe5\u8e85\u9e88",
"zhua": "\u6293",
"zhuai": "\u62fd",
"zhuan": "\u4e13\u7816\u8f6c\u64b0\u8d5a\u7bc6\u629f\u556d\u989b",
"zhuang": "\u6869\u5e84\u88c5\u5986\u649e\u58ee\u72b6\u4e2c",
"zhui": "\u690e\u9525\u8ffd\u8d58\u5760\u7f00\u8411\u9a93\u7f12",
"zhun": "\u8c06\u51c6",
"zhuo": "\u6349\u62d9\u5353\u684c\u7422\u8301\u914c\u707c\u6d4a\u502c\u8bfc\u5ef4\u855e\u64e2\u555c\u6d5e\u6dbf\u6753\u712f\u799a\u65ab",
"zi": "\u5179\u54a8\u8d44\u59ff\u6ecb\u6dc4\u5b5c\u7d2b\u4ed4\u7c7d\u6ed3\u5b50\u81ea\u6e0d\u5b57\u8c18\u5d6b\u59ca\u5b73\u7f01\u6893\u8f8e\u8d40\u6063\u7726\u9531\u79ed\u8014\u7b2b\u7ca2\u89dc\u8a3e\u9cbb\u9aed",
"zong": "\u9b03\u68d5\u8e2a\u5b97\u7efc\u603b\u7eb5\u8159\u7cbd",
"zou": "\u90b9\u8d70\u594f\u63cd\u9139\u9cb0",
"zu": "\u79df\u8db3\u5352\u65cf\u7956\u8bc5\u963b\u7ec4\u4fce\u83f9\u5550\u5f82\u9a75\u8e74",
"zuan": "\u94bb\u7e82\u6525\u7f35",
"zui": "\u5634\u9189\u6700\u7f6a",
"zun": "\u5c0a\u9075\u6499\u6a3d\u9cdf",
"zuo": "\u6628\u5de6\u4f50\u67de\u505a\u4f5c\u5750\u5ea7\u961d\u963c\u80d9\u795a\u9162",
"cou": "\u85ae\u6971\u8f8f\u8160",
"nang": "\u652e\u54dd\u56d4\u9995\u66e9",
"o": "\u5594",
"dia": "\u55f2",
"chuai": "\u562c\u81aa\u8e39",
"cen": "\u5c91\u6d94",
"diu": "\u94e5",
"nou": "\u8028",
"fou": "\u7f36",
"bia": "\u9adf"
};
//在数据中搜索
function search(letter) {
for (var name in py) {
if (py.hasOwnProperty(name)) {
if (py[name].indexOf(letter) != -1) {
return toUCFirst(name);
}
}
}
return false;
}
//转换首字母大写
function toUCFirst(name) {
var first = name.substr(0, 1).toUpperCase();
var spare = name.substr(1, name.length);
return first + spare;
// return first;
}
return {
convert: function (words) {
var long = words.length;
var result = '';
var reg = new RegExp('[a-zA-Z0-9\- ]');
for (var i = 0, letter, name; i < long; i++) {
letter = words.substr(i, 1);
if (reg.test(letter)) {
result += letter;
} else {
name = search(letter);
if (name !== false) {
result += name;
}
}
}
return result.replace(/[ -]+/g, '-');
}
}
})();
if (typeof(exports) == 'object') {
exports.PinYin = PinYin;
}

View File

@ -2,6 +2,4 @@
#
"atom-workspace atom-text-editor:not([mini])":
"shift-ctrl-v": "amWiki:paste"
"ctrl-d": "amWiki:catalogue"
"ctrl-h": "amWiki:history"
"f12": "amWiki:browser"

213
lib/autoNavigation.js Normal file
View File

@ -0,0 +1,213 @@
/**
* 工作端 - Atom - 自动化模块
* @author Tevin
*/
const {CompositeDisposable} = require('atom');
const fs = require('fs');
const crypto = require('crypto');
const makeNav = require('../build/makeNavigation');
const mngFolder = require('../build/manageFolder');
const mngWiki = require('../build/manageWiki');
const autoNavigation = (function () {
return {
/**
* 订阅
* @private
*/
_subscriptions: null,
/**
* atom 树形导航模块的引用
* @private
*/
_treeViewPkgMain: null,
/**
* wiki 列表记录
* @private
*/
_wikis: null,
/**
* 用户相关监听
* @private
*/
_listenUser: function () {
//监听用户普通操作
this._subscriptions = new CompositeDisposable();
this._subscriptions.add(atom.commands.onDidDispatch((e) => {
//重命名、新增文件夹或文件、创建副本等事件
if (/core\:confirm/.test(e.type)) {
this._getCurLib((wId) => {
this._checkLibChange(wId);
});
}
//删除、剪切、粘帖文件事件
else if (/tree-view\:(remove|cut|paste)/.test(e.type)) {
this._getCurLib((wId) => {
this._checkLibChange(wId);
});
}
//删除项目文件夹事件
//else if (e.type === 'tree-view:remove-project-folder') {}
}));
//从atom目录树监听用户拖拽操作
atom.packages.activatePackage('tree-view').then((pkg) => {
this._treeViewPkgMain = pkg.mainModule;
const elm = pkg.mainModule.treeView[0] || pkg.mainModule.treeView.element;
elm.addEventListener('drop', (e) => {
let path, wId;
for (let item of e.path) {
if (item.className.indexOf('project-root') >= 0) {
path = item.querySelector('.header > span').getAttribute('data-path');
wId = mngWiki.createWikiId(path);
if (typeof this._wikis[wId] !== 'undefined') {
this._checkLibChange(wId);
} else {
path = path.replace(/\\/g, '/');
mngWiki.addWiki(path, wId);
this._checkLibChange(wId);
}
break;
}
}
}, false);
});
//当编辑器打开一个文件时的回调
//this._subscriptions.add(atom.workspace.onDidOpen(function () {}));
},
/**
* 变化相关监听
* @private
*/
_listenChange: function () {
//当项目数量变化时的回调(即添加或删除项目)
this._subscriptions.add(atom.project.onDidChangePaths((list) => {
//文库列表比对,添加新增
for (let [i, item] of list) {
item = item.replace(/\\/g, '/') + '/library/';
for (let wId in this._wikis) {
if (this._wikis.hasOwnProperty(wId) && item === this._wikis[wId].path) {
list[i] = null;
}
}
}
for (let item of list) {
if (item && mngFolder.isAmWiki(item)) {
mngWiki.addWiki(item.replace(/\\/g, '/') + '/');
}
}
}));
},
/**
* 时间相关监听
* @private
*/
_listenTime: function () {
//切换窗口出去超过30秒回来时检查目录
let lastTimestamp;
window.addEventListener('blur', () => {
lastTimestamp = Date.now();
});
window.addEventListener('focus', () => {
//如果atom插件设置“得到焦点时检查库变化”项处于开启状态检查当前文库
if (atom.config.get('amWiki.checkLibraryOnWindowFocus')) {
if (Date.now() - lastTimestamp > 30000) {
this._getCurLib((wId) => {
this._checkLibChange(wId);
});
}
}
});
//当atom启动时如果插件设置“启动时全量检查文库变化”项处于开启状态全量检查文库直接检查不判断当前库
if (atom.config.get('amWiki.checkAllLibraryOnAtomStart')) {
for (let wId in this._wikis) {
if (this._wikis.hasOwnProperty(wId)) {
setTimeout(() => {
mngWiki.checkWikiValid(wId);
this._checkLibChange(wId);
}, 100);
}
}
}
},
/**
* 推测当前文库是哪一个
* @param {Function} callback
* @param {Number} recursive - 递归次数标记
* @private
*/
_getCurLib: function (callback, recursive = 1) {
const treeView = this._treeViewPkgMain.treeView, //目录树主对象
treeState = this._treeViewPkgMain.state; //目录树全局状态
let wId = '';
//如果 treeView 已经准备完毕,获取路径
if (treeView && treeView.selectedPath) {
//如果 treeView 当前路径是已经存在的 wiki
wId = mngWiki.createWikiId(treeView.selectedPath.replace(/\\/g, '/').split('library')[0]);
if (typeof this._wikis[wId] !== 'undefined') {
callback(wId);
}
//否则从 treeState 获取路径
else if (treeState && treeState.selectedPath) {
//如果 treeState 当前路径补是已存在的wiki
wId = mngWiki.createWikiId(treeState.selectedPath.replace(/\\/g, '/').split('library')[0]);
if (typeof this._wikis[wId] !== 'undefined') {
callback(wId);
}
//如果 treeState 当前路径也不匹配,不再检查
}
}
//如果_treeView未准备完毕0.2秒后重新检查且最多只递归3次超过后不再检查
else {
setTimeout(() => {
recursive++;
if (recursive <= 3) {
this._getCurLib(callback, recursive);
}
}, 200);
}
},
/**
* 检查文库变化
* @param {String} wId
* @private
*/
_checkLibChange: function (wId) {
//利用延迟跳出执行列队,等待系统文件操作完毕再读取
setTimeout(() => {
if (this._wikis[wId].deprecated) {
return;
}
const [tree, list, files] = mngFolder.readLibraryTree(this._wikis[wId].path);
if (!tree) {
return
}
const libMd5 = crypto.createHash('md5').update(files.join('&')).digest('base64');
if (this._wikis[wId].treeMD5 !== libMd5) {
this._wikis[wId].treeMD5 = libMd5;
//更新导航文件
makeNav.make(this._wikis[wId].path, tree, list);
}
}, 100);
},
/**
* 监听发起
* @param {[Object]} wikis - 文库记录列表的引用
* @public
*/
start: function (wikis) {
this._wikis = wikis;
this._listenUser();
this._listenChange();
this._listenTime();
},
/**
* 监听释放
* @public
*/
destroy: function () {
this._subscriptions && this._subscriptions.dispose();
}
};
})();
module.exports = autoNavigation;

View File

@ -1,192 +0,0 @@
/**
* @desc amWiki 工作端·自动化模块
* @author Tevin
*/
var environment = require('atom'),
Directory = environment.Directory,
CompositeDisposable = environment.CompositeDisposable;
var fs = require('fs');
var makeNav = require('./makeNav');
var directories = require('./directories');
var crypto = require('crypto');
module.exports = {
//订阅
subscriptions: null,
//目录树的引用
treeView: null,
//当文库数量变化时
onLibrariesChange: null,
//监听发起
start: function (state) {
if (state.libraryList.length == 0) {
return;
}
var that = this;
//检查library是否仍然存在删除不存在的库
state.libraryList = directories.eachLibrary(state.libraryList);
var libraryMD5 = {};
for (var i = 0; i < state.libraryList.length; i++) {
libraryMD5[state.libraryList[i]] = state.libraryMD5[state.libraryList[i]];
}
state.libraryMD5 = libraryMD5;
//监听用户操作
this.subscriptions = new CompositeDisposable();
this.subscriptions.add(atom.commands.onDidDispatch(function (e) {
//console.log('commands:', e.type);
//重命名、新增文件夹或文件、创建副本事件
if (/core\:confirm/.test(e.type) && e.target.className == 'editor mini is-focused') {
that.toCheckLib(state);
}
//删除、剪切、粘帖文件事件
else if (/tree-view\:(remove|cut|paste)/.test(e.type)) {
that.toCheckLib(state);
}
//删除项目文件夹事件
else if (e.type == 'tree-view:remove-project-folder') {
}
}));
//当编辑器打开一个文件时的回调
this.subscriptions.add(atom.workspace.onDidOpen(function () {
}));
//当项目(路径)数量变化时的回调(即添加或删除项目)
this.subscriptions.add(atom.project.onDidChangePaths(function (list) {
//文库列表比对,移除失效,添加新增
var list2 = [];
for (var i = 0; i < list.length; i++) {
list[i] = list[i].replace(/\\/g, '/') + '/library/';
for (var j = 0; j < state.libraryList.length; j++) {
if (list[i] == state.libraryList[j]) {
list2.push(state.libraryList[j]);
list[i] = null;
}
}
}
for (var k = 0; k < list.length; k++) {
if (list[k] && directories.isAmWiki(list[k])) {
list2.push(list[k]);
}
}
state.libraryList = list2;
//MD5同步变化
var libraryMD5 = {};
for (var ii = 0; ii < state.libraryList.length; ii++) {
libraryMD5[state.libraryList[ii]] = state.libraryMD5[state.libraryList[ii]];
}
state.libraryMD5 = libraryMD5;
//回调通知 main.js 文库数已变化
that.onLibrariesChange && that.onLibrariesChange();
}));
//从树形模块监听拖拽操作
atom.packages.activatePackage('tree-view').then(function (pkg) {
that.treeView = pkg.mainModule.treeView; //目录树主对象
that.treeState = pkg.mainModule.state; //目录树全局状态
that.treeView[0].addEventListener('drop', function (e) {
for (var i = 0; i < e.path.length; i++) {
if (e.path[i].className.indexOf('project-root') >= 0) {
var path = e.path[i].querySelector('.header > span').getAttribute('data-path');
that.toCheckLib(state, path.replace(/\\/g, '/') + '/library/');
break;
}
}
}, false);
});
//切换窗口出去超过30秒回来时检查目录
var lastTimestamp;
window.addEventListener('blur', function () {
lastTimestamp = Date.now();
});
window.addEventListener('focus', function () {
//如果atom插件设置“得到焦点时检查库变化”项处于开启状态
if (atom.config.get('amWiki.checkLibraryOnWindowFocus')) {
if (Date.now() - lastTimestamp > 30000) {
that.toCheckLib(state);
}
}
});
//当atom启动时如果插件设置“启动时全量检查文库变化”项处于开启状态全量检查文库
if (atom.config.get('amWiki.checkAllLibraryOnAtomStart')) {
for (var l = 0; l < state.libraryList.length; l++) {
this.checkLibrary(state, state.libraryList[l]);
}
}
},
//准备检查文库
toCheckLib: function (state, curLibrary, recursive) {
var that = this;
//当存在curLibrary参数时直接检查此文库
if (curLibrary) {
this.checkLibrary(state, curLibrary);
}
//当不存在curLibrary参数时尝试获取当前库
else {
//如果treeView已经准备完毕
if (this.treeView && this.treeView.selectedPath) {
curLibrary = this.treeView.selectedPath.replace(/library(.|\s)*$/, 'library/').replace(/\\/g, '/');
//如果treeView当前路径包含library
if (curLibrary && curLibrary.indexOf('library') >= 0) {
this.checkLibrary(state, curLibrary);
}
//如果treeView当前路径补/library/字段后是已存在的wiki
else if (typeof state.libraryMD5[curLibrary + '/library/'] != 'undefined') {
this.checkLibrary(state, curLibrary + '/library/');
}
//从treeState获取路径
else if (this.treeState && this.treeState.selectedPath) {
curLibrary = this.treeState.selectedPath.replace(/library(.|\s)*$/, 'library/').replace(/\\/g, '/');
//如果treeState当前路径包含library
if (curLibrary && curLibrary.indexOf('library') >= 0) {
this.checkLibrary(state, curLibrary);
}
//如果treeState当前路径补/library/字段后是已存在的wiki
else if (typeof state.libraryMD5[curLibrary + '/library/'] != 'undefined') {
this.checkLibrary(state, curLibrary + '/library/');
}
//如果treeState当前路径也不匹配不检查文库更新
}
}
//如果treeView未准备完毕0.2秒后重新检查且最多只递归3次
else {
setTimeout(function () {
recursive = recursive ? recursive + 1 : 1;
if (recursive <= 3) {
that.toCheckLib(state, curLibrary, recursive);
}
}, 200);
}
}
},
//检查文库变化
checkLibrary: function (state, curLibrary) {
curLibrary = curLibrary.split('library/')[0] + 'library/';
var libHas = false;
for (var i = 0; i < state.libraryList.length; i++) {
if (state.libraryList[i] == curLibrary) {
libHas = true;
}
}
if (!libHas) {
return;
}
//延迟0.1秒,等待系统文件操作完毕再读取
setTimeout(function () {
directories.readLibraryDir(curLibrary, function (err, tree, files) {
if (err) {
console.warn(err);
} else {
var libMd5 = crypto.createHash('md5').update(files.join('&')).digest('base64');
if (state.libraryMD5[curLibrary] != libMd5) {
state.libraryMD5[curLibrary] = libMd5;
//更新导航文件
makeNav.make(curLibrary, tree);
}
}
});
}, 100);
},
//释放监听
destroy: function () {
this.subscriptions && this.subscriptions.dispose();
}
};

View File

@ -1,67 +0,0 @@
/**
* @desc amWiki 工作端·提取H2-H3为页内目录模块
* @author Tevin
*/
module.exports = {
//创建
make: function () {
var editor = atom.workspace.getActiveTextEditor();
if (!editor) {
return;
}
var grammar = editor.getGrammar();
if (!grammar) {
return;
}
if (grammar.scopeName !== 'source.gfm' && grammar.scopeName !== 'text.md') {
return;
}
editor.insertText(this.extract(editor.getText()) || '');
},
//抽取标题
extract: function (arc) {
var lines = arc.split('\n');
if (lines.length == 1) {
lines = lines[0].split('\r');
}
var contents = '';
var lineStr = '';
var hashStr = '';
var h1 = '';
for (var i = 0; i < lines.length; i++) {
lines[i] = lines[i].replace(/^\s*/, '');
switch (lines[i].split(/\s/)[0]) {
case '#':
var text = lines[i].split('# ')[1];
if (text != undefined) {
lineStr = text.replace(/^\s+|\s+$/g, ''); //去除首位空格
h1 = h1 == '' ? lineStr.replace(/\[(.*?)\]\(.*?\)/g, '$1') : h1;
}
break;
case '##':
var text = lines[i].split('## ')[1];
if (text != undefined) {
lineStr = text.replace(/^\s+|\s+$/g, '');
lineStr = lineStr.replace(/\[(.*?)\]\(.*?\)/g, '$1'); //去除链接
contents += '1. [' + lineStr + '](#' + lineStr + ' "' + lineStr + '")\n';
}
break;
case '###':
var text = lines[i].split('### ')[1];
if (text != undefined) {
lineStr = text.replace(/^\s+|\s+$/g, '');
lineStr = lineStr.replace(/\[(.*?)\]\(.*?\)/g, '$1');
contents += '\t1. [' + lineStr + '](#' + lineStr + ' "' + lineStr + '")\n';
}
break;
}
}
if (contents.substr(0, 2) != '1.') {
contents = '1. ' + h1 + '\n' + contents;
}
contents = '>' + contents;
return contents;
}
};

View File

@ -1,276 +0,0 @@
/**
* @desc amWiki 工作端·输出web静态资源模块
* @author Tevin
*/
var fs = require("fs");
var makeNav = require('./makeNav');
//复制文件
var copyFile = function (from, to) {
var encoding = from.indexOf('png') >= 0 ? 'binary' : 'utf-8';
var file = fs.readFileSync(from, encoding);
fs.writeFileSync(to, file, encoding);
};
//创建amWiki需要的文件夹
var createDir = function (outputPath) {
if (!fs.existsSync(outputPath + 'amWiki/')) {
fs.mkdirSync(outputPath + 'amWiki/', 0777);
}
if (!fs.existsSync(outputPath + 'amWiki/js/')) {
fs.mkdirSync(outputPath + 'amWiki/js/', 0777);
}
if (!fs.existsSync(outputPath + 'amWiki/css')) {
fs.mkdirSync(outputPath + 'amWiki/css', 0777);
}
if (!fs.existsSync(outputPath + 'amWiki/images')) {
fs.mkdirSync(outputPath + 'amWiki/images', 0777);
}
if (!fs.existsSync(outputPath + 'library/')) {
fs.mkdirSync(outputPath + 'library/', 0777);
if (!fs.existsSync(outputPath + 'library/001-学习amWiki')) {
fs.mkdirSync(outputPath + 'library/001-学习amWiki', 0777);
}
if (!fs.existsSync(outputPath + 'library/001-学习amWiki/05-学习markdown')) {
fs.mkdirSync(outputPath + 'library/001-学习amWiki/05-学习markdown', 0777);
}
if (!fs.existsSync(outputPath + 'library/002-文档示范')) {
fs.mkdirSync(outputPath + 'library/002-文档示范', 0777);
}
return false;
}
return true;
};
//创建着色色系颜色变亮变暗不是rgb分别同时增加或减少一个值
var makeColours = function (color) {
var colours = {};
//修复16进制下的1位数
var lessThan = function (str) {
if (str <= 9) {
str = '0' + str;
} else {
var num = parseInt('0x' + str);
if (num >= 10 && num <= 15) {
str = '0' + str;
}
}
return str;
};
//颜色合法检查
var atBinary16 = /^#([0-9a-fA-F]{3}){1,2}$/.test(color);
var atRGB = /^(r|R)(g|G)(b|B)(a|A)?\(\d{1,3},\d{1,3},\d{1,3}(,.*?)?\)$/.test(color);
var atList = /(blue|default)/.test(color);
if (!atBinary16 && !atRGB && !atList) {
alert('请使用标准16进制颜色、标准RGB颜色、2种特定颜色名指定颜色本次创建使用默认颜色');
return makeColours('default');
}
//颜色名称转色值
var colorList = {
default: '#4296eb',
blue: '#4296eb'
};
if (atList) {
color = colorList[color];
}
//解析颜色为rgb
var rgb = [];
if (color.indexOf('#') == 0) {
rgb[0] = parseInt('0x' + color.substr(1, 2));
rgb[1] = parseInt('0x' + color.substr(3, 2));
rgb[2] = parseInt('0x' + color.substr(5, 2));
} else {
var rgbStr = color.split('(')[1].split(')')[0].split(',');
rgb[0] = parseInt(rgbStr[0]);
rgb[1] = parseInt(rgbStr[1]);
rgb[2] = parseInt(rgbStr[2]);
}
//生成主色base
colours.base = '#' + [
lessThan(rgb[0].toString(16)),
lessThan(rgb[1].toString(16)),
lessThan(rgb[2].toString(16))
].join('');
//颜色变化量,越小的值变化越大
var increment = [
Math.round((255 - rgb[0]) / 3),
Math.round((255 - rgb[1]) / 3),
Math.round((255 - rgb[2]) / 3)
];
//生成亮色light
colours.light = '#' + [
(rgb[0] + increment[0]).toString(16),
(rgb[1] + increment[1]).toString(16),
(rgb[2] + increment[2]).toString(16)
].join('');
//生成暗色dark
var larger = 0,
largeIndex = -1;
var largerFd = function (i) {
if (increment[i] > larger) {
larger = increment[i];
largeIndex = i;
}
};
largerFd(0);
largerFd(1);
largerFd(2);
if (rgb[largeIndex] - larger < 0) {
//修复加深颜色值为负的情况最多减到0其他三色等比例减小
var reduce = (rgb[largeIndex] - larger) / larger;
increment[0] += Math.round(increment[0] * reduce);
increment[1] += Math.round(increment[1] * reduce);
increment[2] += Math.round(increment[2] * reduce);
}
colours.dark = '#' + [
lessThan((rgb[0] - increment[0]).toString(16)),
lessThan((rgb[1] - increment[1]).toString(16)),
lessThan((rgb[2] - increment[2]).toString(16))
].join('');
return colours;
};
module.exports = {
//开始创建
create: function (state) {
var options = {};
var editor = atom.workspace.getActiveTextEditor();
if (!editor) {
return;
}
options.editorPath = editor.getPath();
if (options.editorPath.indexOf('config.json') < 0) {
alert('当前不是"config.json"文件!');
return;
}
//创建路径
options.filesPath = atom.configDirPath.replace(/\\/g, '/') + '/packages/amWiki/files/';
options.outputPath = options.editorPath.split('config.json')[0].replace(/\\/g, '/');
//读取配置
var config = fs.readFileSync(options.editorPath, 'utf-8') || '';
if (config.length == 0) {
if (!confirm('没有读取到任何配置,继续创建么?')) {
return;
} else {
config = "{}";
}
}
//解析默认配置
var parseOk = true;
try {
config = JSON.parse(config);
} catch (e) {
alert('配置解析失败,请检查您的 config.json 是否有严格按 Json 格式书写!\n错误消息' + e.message);
parseOk = false;
}
if (!parseOk) {
return;
}
config.name = config.name || 'amWiki轻文库系统'; //库名称
config.version = typeof config.ver == 'string' ? config.ver : 'by Tevin'; //库版本号
config.logo = config.logo || 'amWiki/images/logo.png'; //logo地址
config.testing = config.testing || false; //是否开启接口测试
config.colour = config.colour ? makeColours(config.colour) : makeColours('#4296eb'); //设置自定义颜色
//开始创建
this.buildAt(options, config, function (path) {
//添加文库
state.libraryList.push(path);
//更新导航
makeNav.refresh(options.outputPath + 'library/');
});
},
//创建amWiki本地文件
buildAt: function (options, config, callback) {
//创建
fs.readdir(options.outputPath, function (err, files) {
if (files.length > 1) {
if (!confirm('此处已有一些文件或文件夹是否仍然在此创建amWiki')) {
return;
}
}
//创建index.html
var indexPage = fs.readFileSync(options.filesPath + 'amWiki.tpl', 'utf-8');
indexPage = indexPage.replace(/\{\{name\}\}/g, config.name)
.replace('{{version}}', config.version)
.replace('{{logo}}', config.logo);
if (config.testing) {
var testingTpl = fs.readFileSync(options.filesPath + 'amWiki.testing.tpl', 'utf-8');
var testingScript = '<script src="amWiki/js/amWiki.testing.js"></script>';
indexPage = indexPage
.replace('{{amWiki.testing.tpl}}', testingTpl)
.replace('{{amWiki.testing.js}}', testingScript);
} else {
indexPage = indexPage
.replace('{{amWiki.testing.tpl}}', '')
.replace('{{amWiki.testing.js}}', '');
}
fs.writeFileSync(options.outputPath + 'index.html', indexPage, 'utf-8');
//创建文件夹
var hasLibrary = createDir(options.outputPath);
//创建amWiki.css
var wikiCss = fs.readFileSync(options.filesPath + 'amWiki.css', 'utf-8');
if (config.testing) {
wikiCss += fs.readFileSync(options.filesPath + 'amWiki.testing.css', 'utf-8');
}
wikiCss += fs.readFileSync(options.filesPath + 'amWiki.print.css', 'utf-8');
wikiCss += fs.readFileSync(options.filesPath + 'amWiki.search.css', 'utf-8');
wikiCss = wikiCss
.replace(/@colour-base/g, config.colour.base)
.replace(/@colour-light/g, config.colour.light)
.replace(/@colour-dark/g, config.colour.dark);
fs.writeFileSync(options.outputPath + 'amWiki/css/amWiki.css', wikiCss, 'utf-8');
//拷贝页面资源
var fileList = [
['markdownbody.github.css', 'amWiki/css/markdownbody.github.css'],
['lhjs.github-gist.css', 'amWiki/css/lhjs.github-gist.css'],
['gbk.js', 'amWiki/js/gbk.js'],
['flowchart.min.js', 'amWiki/js/flowchart.min.js'],
['raphael-min.js', 'amWiki/js/raphael-min.js'],
['jquery-compat-3.1.0.min.js', 'amWiki/js/jquery-compat-3.1.0.min.js'],
['marked.min.js', 'amWiki/js/marked.min.js'],
['highlight.min.js', 'amWiki/js/highlight.min.js'],
['amWiki.js', 'amWiki/js/amWiki.js'],
['amWiki.testing.js', 'amWiki/js/amWiki.testing.js'],
['amWiki.tools.js', 'amWiki/js/amWiki.tools.js'],
['amWiki.storage.js', 'amWiki/js/amWiki.storage.js'],
['amWiki.docs.js', 'amWiki/js/amWiki.docs.js'],
['amWiki.search.js', 'amWiki/js/amWiki.search.js'],
['amWiki.search.worker.js', 'amWiki/js/amWiki.search.worker.js'],
['icons.svg', 'amWiki/images/icons.svg'],
['logo.png', 'amWiki/images/logo.png'],
['menubar_bg.png', 'amWiki/images/menubar_bg.png']
];
for (var i = 0; i < fileList.length; i++) {
copyFile(options.filesPath + fileList[i][0], options.outputPath + fileList[i][1]);
}
//如果没有library则复制一套默认文档
if (!hasLibrary) {
//首页文档
var home = fs.readFileSync(options.filesPath + 'doc.home.md', 'utf-8');
home = home.replace('{{name}}', config.name);
fs.writeFileSync(options.outputPath + 'library/首页.md', home, 'utf-8');
//其他页面文档
var fileList2 = [
['doc.amwiki-introduce.md', 'library/001-学习amWiki/01-amWiki轻文库简介.md'],
['doc.amwiki-mind-map.md', 'library/001-学习amWiki/02-amWiki功能导图.md'],
['doc.amwiki-new.md', 'library/001-学习amWiki/03-如何开始一个新amWiki轻文库.md'],
['doc.amwiki-edit.md', 'library/001-学习amWiki/04-如何编辑amWiki轻文库.md'],
['doc.amwiki-testing.md', 'library/001-学习amWiki/06-使用测试模块测试接口.md'],
['doc.amwiki-test-cross.md', 'library/001-学习amWiki/07-amWiki转接到任意域名进行接口测试.md'],
['doc.md-start.md', 'library/001-学习amWiki/05-学习markdown/01-Markdown快速开始.md'],
['doc.md-high-lighting.md', 'library/001-学习amWiki/05-学习markdown/02-amWiki与语法高亮.md'],
['doc.md-flow-chart.md', 'library/001-学习amWiki/05-学习markdown/03-amWiki与流程图.md'],
['doc.md-atom.md', 'library/001-学习amWiki/05-学习markdown/05-Atom对Markdown的原生支持.md'],
['doc.demo-api.md', 'library/002-文档示范/001-通用API接口文档示例.md'],
['doc.demo-long-article.md', 'library/002-文档示范/002-超长文档页内目录示例.md']
];
for (var j = 0; j < fileList2.length; j++) {
copyFile(options.filesPath + fileList2[j][0], options.outputPath + fileList2[j][1]);
}
}
callback && callback(options.outputPath + 'library/');
});
}
};

View File

@ -1,131 +0,0 @@
/**
* @desc amWiki 工作端·文件夹管理模块
* @author Tevin
*/
var fs = require('fs');
module.exports = {
//读取文库文件夹
readLibraryDir: function (path, callback) {
if (!/library(\\|\/)$/.test(path)) {
callback('The path is not a library.');
return;
}
var tree = {};
var folders = [];
var files = [];
try {
var files1 = fs.readdirSync(path),
files2, files3,
path2, path3, path4;
folders.push(path);
//第一层files1library直接子级仅允许为文件夹
for (var i = 0; i < files1.length; i++) {
path2 = path + files1[i];
if (/^\./.test(files1[i])) {
continue;
}
if (fs.statSync(path2).isDirectory(path2)) {
try {
files2 = fs.readdirSync(path2);
folders.push(path2);
tree[files1[i]] = {};
//第二层files2允许为文件夹和文件
for (var j = 0; j < files2.length; j++) {
path3 = path2 + '/' + files2[j];
if (/^\./.test(files2[j])) {
continue;
}
if (fs.statSync(path3).isDirectory(path3)) {
try {
files3 = fs.readdirSync(path3);
folders.push(path3);
tree[files1[i]][files2[j]] = {};
//第三层files3仅允许为文件夹不再深入
for (var k = 0; k < files3.length; k++) {
path4 = path3 + '/' + files3[k];
if (/^\./.test(files3[k])) {
continue;
}
if (!fs.statSync(path4).isDirectory(path4)) {
tree[files1[i]][files2[j]][files3[k]] = false;
files.push(path4);
}
}
} catch (err) {
callback(err);
return;
}
} else {
tree[files1[i]][files2[j]] = false;
files.push(path3);
}
}
} catch (err) {
callback(err);
return;
}
}
}
} catch (err) {
callback(err);
return;
}
callback(null, tree, files, folders);
},
//循环检查每个库
eachLibrary: function (list, stepCallback) {
var list2 = [];
var path = '';
var atomProjects = atom.project.getPaths();
for (var i = 0; i < list.length; i++) {
//路径缺乏library字段弃用
if (list[i].indexOf('library') < 0) {
continue;
}
path = list[i].replace(/\\/g, '/').split('library')[0] + 'library/';
//路径不存在弃用
if (!fs.existsSync(path)) {
continue;
}
//路径重复弃用
var pathRepeat = false;
for (var j = 0; j < list2.length; j++) {
if (list2[j] == list[i]) {
pathRepeat = true;
break;
}
}
if (pathRepeat) {
continue;
}
//如果atom已经移除此项目弃用
/*var pathUseful = false;
for (var k = 0; k < atomProjects.length; k++) {
if (atomProjects[k].replace(/\\/g, '/') + '/library/' == path) {
pathUseful = true;
break;
}
}
if (!pathUseful) {
continue;
}*/
//有用路径
list2.push(path);
stepCallback && stepCallback(list[i]);
}
return list2;
},
//判断一个文件夹是否为amWiki文库项目
isAmWiki: function (path) {
path = path.indexOf('library') < 0 ? path : path.split('library')[0];
var states = [
fs.existsSync(path + '/library/'),
fs.existsSync(path + '/amWiki/'),
fs.existsSync(path + '/config.json'),
fs.existsSync(path + '/index.html')
];
return states[0] && states[1] && states[2] && states[3];
}
};

100
lib/dropFile.js Normal file
View File

@ -0,0 +1,100 @@
/**
* 工作端 - Atom - 文件拖拽模块
* @author Tevin
*/
const fs = require('fs');
const mngFolder = require('../build/manageFolder');
const richPaste = require('./richPaste');
const pinyin = require('../files/pinyin.js').PinYin;
const dropFile = (function () {
return {
/**
* 转存获取编辑器当前文档状态的方法
* @private
*/
_getEditorPath: null,
/**
* 拖拽图片
* @param {[Object]} files - 鼠标拖拽的文件列表
* @param {String} createDirPath - 输出的目录
* @param {String} insertText - 插入的文本
* @param {Object} editor - 编辑器的引用
* @private
*/
_dropImg: function (files, createDirPath, insertText, editor) {
mngFolder.createFolder(createDirPath);
//按天区分文件
const date = new Date();
let month = date.getMonth() + 1;
let day = date.getDate();
month = (month <= 9 ? '0' : '') + month;
day = (day <= 9 ? '0' : '') + day;
//输出
for (let file of files) {
let ext = 'jpg';
let name = date.getFullYear() + month + day + '-';
//将名称中的汉字转换为拼音,以避免跨平台文件名编码不同,造成无法访问的问题
name += pinyin.convert(file.name.replace(/\.([^.]+?)$/, function (m, s1) {
ext = s1;
return '';
}));
fs.createReadStream(file.path).pipe(fs.createWriteStream(createDirPath + name + '.' + ext));
insertText = '![](' + insertText + name + '.' + ext + ' "' + file.name.replace(/\.([^.]+?)$/, '') + '") \n';
editor.insertText(insertText, editor);
}
},
/**
* 监听拖拽
* @param {Function} getEditorPath - 获取编辑器当前文档状态的方法的引用
* @public
*/
listenDrop: function (getEditorPath) {
this._getEditorPath = getEditorPath;
atom.document.getElementsByClassName('item-views')[0].addEventListener('drop', (e) => {
//TODO: console.log(e.dataTransfer.getData("treeViewDrag"));
//文件验证,包含非图片文件跳过不工作
const files = e.dataTransfer.files;
let allImg = true;
for (let file of files) {
if (file.type.indexOf('image/') !== 0) {
allImg = false;
}
}
if (!allImg) {
return;
}
//编辑器状态验证,非 markdown 不工作
const [isOnEdit, editor, editPath] = this._getEditorPath();
if (!isOnEdit) {
return;
}
const [assetsDirPath, createDirPath, insertText] = richPaste.getPastePaths(editPath);
if (!assetsDirPath) {
return;
}
//验证路径,非 amWiki 不工作
if (!mngFolder.isAmWiki(editPath)) {
return;
}
//此时已经匹配拖拽条件,阻止默认动作
e.stopPropagation();
//执行拖拽图片
this._dropImg(files, createDirPath, insertText, editor);
});
/* TODO:
atom.packages.activatePackage('tree-view').then(function (pkg) {
this.treeView = pkg.mainModule.treeView;
this.treeView[0].addEventListener('dragstart', function (e) {
let path = e.target.childNodes[0].getAttribute('data-path');
e.dataTransfer.setData("treeViewDrag", path);
});
});*/
}
};
})();
module.exports = dropFile;

View File

@ -1,85 +1,291 @@
/**
* @desc amWiki 工作端·模块
* @author Tevin
* 工作端 - Atom - 入口模块
* @author Tevin Li
* @see {@link https://github.com/TevinLi/amWiki}
* @license MIT - Released under the MIT license.
*/
//引入-运行环境
var environment = require('atom'),
CompositeDisposable = environment.CompositeDisposable;
//引入-手动刷新工具
var makeNav = require('./makeNav');
//引入-自动化工具
var automatic = require('./automatic');
//引入-wiki创建器
var creator = require('./creator');
//引入-页内目录创建器
var catalogue = require('./catalogue');
//引入-截图粘帖模块
var pasterImg = require('./pasterImg');
//引入-本地服务器模块
var webServer = require('./webServer');
//Atom 层运行环境接口
const {CompositeDisposable} = require('atom');
const fs = require('fs');
//Electron 层 shell 接口
//系统对话框
const {dialog: eDialog} = require('electron').remote;
module.exports = {
//订阅
subscriptions: null,
//缓存atom存储的状态变量
state: null,
//当atom启动时
activate: function (state) {
var that = this;
this.state = state;
//文库列表记录
this.state.libraryList = this.state.libraryList || [];
//文库列表地址MD5检查文库变化时创建
this.state.libraryMD5 = this.state.libraryMD5 || {};
//amWiki命令
this.subscriptions = new CompositeDisposable();
this.subscriptions.add(atom.commands.add('atom-workspace', {
//命令基于当前config.json创建wiki
'amWiki:create': function () {
creator.create(that.state);
},
//命令在当前文档上抓取h2、h3为页内目录
'amWiki:catalogue': function () {
catalogue.make();
},
//命令,手动更新当前文库导航
'amWiki:makeNav': function () {
makeNav.update(that.state);
},
//命令,粘帖截图
'amWiki:paste': function () {
pasterImg.paster();
},
//命令启动node静态服务器
'amWiki:runServer': function () {
webServer.run(that.state.libraryList);
},
//命令,浏览打开当前页面
'amWiki:browser': function () {
webServer.browser(that.state.libraryList);
//co模块异步函数编程
const co = require('../modules/co');
//wiki创建器
const creator = require('../build/creator');
//文库管理
const mngWiki = require('../build/manageWiki');
//文件夹管理
const mngFolder = require('../build/manageFolder');
//手动刷新导航文件
const makeNav = require('../build/makeNavigation');
//手动刷新页面挂载数据
const makeMut = require('../build/makeMounts');
//本地服务器模块
const localServer = require('../build/localServer');
//项目导出模块
const exportGithub = require('../build/exportGithub');
//自动化工具
const autoNav = require('./autoNavigation');
//页内目录创建器
const pageCatalogue = require('./pageCatalogue');
//截图粘帖模块
const richPaste = require('./richPaste');
//文件拖拽模块
const dropFile = require('./dropFile');
const main = (function () {
return {
/**
* 订阅
* @private
*/
_subscriptions: null,
/**
* 缓存atom存储的配置
* @private
*/
_config: null,
/**
* 文库记录
* @private
*/
_wikis: null,
/**
* 当atom启动时
* @param {Object} state
* @public
*/
activate: function (state) {
this._config = state.config;
//文库记录
this._wikis = state.wikis || {};
mngWiki.linkWikis(this._wikis);
//检查文库记录有效性
for (let wId in this._wikis) {
if (this._wikis.hasOwnProperty(wId)) {
mngWiki.checkWikiValid(wId);
}
}
}));
//自动化模块启动
automatic.start(this.state);
automatic.onLibrariesChange = function () {
webServer.updateMap(that.state.libraryList);
};
window.amWikiState = state;
},
//当atom即将关闭终止所有监听
deactivate: function () {
webServer.destroy();
automatic.destroy();
this.subscriptions.dispose();
},
//当atom窗口被挂起保存文库列表
serialize: function () {
return {
libraryList: this.state.libraryList,
libraryMD5: this.state.libraryMD5
};
}
};
//转换旧版数据
if (state.libraryList) {
for (let i = 0, item, itemPath, itemRoot; itemPath = state.libraryList[i]; i++) {
itemRoot = itemPath.split('library')[0];
item = {
id: mngWiki.createWikiId(itemRoot),
root: itemRoot,
path: itemPath
};
if (typeof state.libraryMD5[itemPath] !== 'undefined') {
item.treeMD5 = state.libraryMD5[itemPath];
}
this._wikis[item.id] = item;
}
}
//amWiki命令
this._subscriptions = new CompositeDisposable();
this._subscriptions.add(atom.commands.add('atom-workspace', {
//命令基于当前config.json创建wiki
'amWiki:create': () => {
const editor = atom.workspace.getActiveTextEditor();
if (!editor) {
return;
}
const editPath = editor.getPath();
if (editPath.indexOf('config.json') < 0) {
alert('创建失败,当前不是"config.json"文件!');
return;
}
const packagePath = atom.configDirPath.replace(/\\/g, '/') + '/packages/amWiki/';
const packageConf = JSON.parse(fs.readFileSync(packagePath + 'package.json', 'utf-8'));
creator.create(editPath, packagePath + 'files/', packageConf).then((root) => {
if (!root) {
return;
}
const wId = mngWiki.createWikiId(root);
//添加文库
if (typeof this._wikis[wId] === 'undefined') {
mngWiki.addWiki(root, wId);
}
//文库已存在时,修改弃用标记为启用
else {
mngWiki.updateWikiConfig();
this._wikis[wId].deprecated = false;
}
//更新导航
makeNav.refresh(root + 'library/');
//更新挂载数据
makeMut.make(root, true);
});
},
//命令在当前文档上抓取h2、h3为页内目录
'amWiki:catalogue': () => {
const [isOnEdit, editor, editPath] = this._getEditorPath();
if (isOnEdit) {
mngWiki.checkAddWiki(editPath);
pageCatalogue.make(editor);
} else {
alert('只有当你打开 library 文件夹下的 .md 文档时,才能提取页内目录!');
}
},
//命令:手动更新当前文库导航
'amWiki:makeNav': () => {
const [isOnEdit, editor, editPath] = this._getEditorPath();
if (typeof editPath !== 'undefined') {
//当 isOnEdit 为 false 时editPath 仍然不一定为根目录
const root = mngFolder.isAmWiki(editPath);
if (root) {
mngWiki.checkAddWiki(root);
makeNav.refresh(root + 'library/');
return;
}
}
alert('只有当你打开一个 amWiki 文库时,才能手动更新导航文件!');
},
//命令:手动刷新页面挂载数据
'amWiki:makeMut': () => {
const [isOnEdit, editor, editPath] = this._getEditorPath();
if (typeof editPath !== 'undefined') {
//当 isOnEdit 为 false 时editPath 仍然不一定为根目录
const root = mngFolder.isAmWiki(editPath);
if (root) {
mngWiki.checkAddWiki(root);
makeMut.make(root);
return;
}
}
alert('只有当你打开一个 amWiki 文库时,才能更新页面挂载数据!');
},
//命令:粘帖截图
'amWiki:paste': (e) => {
const [isOnEdit, editor, editPath] = this._getEditorPath();
if (isOnEdit) {
mngWiki.checkAddWiki(editPath);
richPaste.paste(editor, editPath);
} else {
if (mngFolder.isAmWiki(editPath)) {
alert('只有当你打开 library 文件夹下的 .md 文档时,才能快捷粘贴图片!');
}
}
},
//命令启动node静态服务器
'amWiki:runServer': () => {
localServer.run(this._wikis);
},
//命令:浏览打开当前页面
'amWiki:browser': () => {
const [isOnEdit, editor, editPath] = this._getEditorPath();
if (isOnEdit) {
mngWiki.checkAddWiki(editPath);
localServer.browser(editPath, this._wikis);
} else {
alert('您需要先打开一篇文档才能浏览!');
}
},
//命令:导出项目为 github wiki
'amWiki:export.gitHub': () => {
const [isOnEdit, editor, editPath] = this._getEditorPath();
if (typeof editPath !== 'undefined') {
const root = mngFolder.isAmWiki(editPath);
if (root) {
mngWiki.checkAddWiki(root);
//选取导出地址
eDialog.showOpenDialog({properties: ['openDirectory']}, (data) => {
if (data && data.length) {
//开始导出
exportGithub.export(root, data[0].replace(/\\/g, '/'));
}
});
return;
}
}
alert('导出失败!\n只能对一个 amWiki 项目进行导出!');
},
//命令显示about
'amWiki:about': () => {
this._showAbout();
}
}));
//自动化模块启动
autoNav.start(this._wikis);
dropFile.listenDrop(this._getEditorPath);
//绑定到全局 window
window.amWikiState = {
config: this._config,
wikis: this._wikis,
clearWikis: () => this._wikis = {}
};
//为配合命令行板块,使用基于 promise 的 confirm2 与 prompt2 替代同步执行的 confirm 与 prompt
window.confirm2 = (msg) => {
return new Promise((resolve, reject) => {
resolve(confirm(msg));
});
};
//window.prompt2 = (msg) => {};
},
/**
* 当atom即将关闭终止所有监听
* @public
*/
deactivate: function () {
localServer.destroy();
autoNav.destroy();
this._subscriptions.dispose();
window.amWikiState = null;
},
/**
* 当atom窗口被挂起保存文库列表
* @returns {{config: Object, wikis: [Object]}}
* @public
*/
serialize: function () {
return {
config: this._config,
wikis: this._wikis
};
},
/**
* 显示 amWiki 相关信息
* @private
*/
_showAbout: function () {
atom.packages.activatePackage('amWiki').then((pkg) => {
const content = 'amWiki v' + pkg.metadata.version + '\n' +
'https://github.com/TevinLi/amWiki\n' +
'MIT Copyright(c) 2016-2099 Tevin Li, amwiki.org.\n';
alert(content);
//TODO: 升级关于弹窗
//var c = atom.workspace.addModalPanel({item: document.createElement('div'), className: 'amWiki-about'})
});
},
/**
* 获取当前文档路径先检查当前是否处于文档编辑状态才返回
* @returns {[Boolean|Object|String]}
* @private
*/
_getEditorPath: function () {
const editor = atom.workspace.getActiveTextEditor();
if (!editor) {
return [false];
}
const grammar = editor.getGrammar();
if (!grammar) {
return [false, editor];
}
const ePath = editor.getPath().replace(/\\/g, '/');
if (ePath.substr(-3) !== '.md') {
return [false, editor, ePath];
}
if (ePath.indexOf('library') < 0) {
return [false, editor, ePath];
}
return [true, editor, ePath];
}
};
})();
module.exports = main;

View File

@ -1,173 +0,0 @@
/**
* @desc amWiki 工作端·创建$navigation导航文件模块
* @author Tevin
*/
var fs = require("fs");
var directories = require('./directories');
module.exports = {
//手动更新导航
update: function (state, callback) {
var editor = atom.workspace.getActiveTextEditor();
if (!editor) {
return;
}
var grammar = editor.getGrammar();
if (!grammar) {
alert('只有当你打开library文件夹下的文档时才能手动更新导航文件');
return;
}
if (grammar.scopeName !== 'source.gfm' && grammar.scopeName !== 'text.md') {
alert('只有当你打开library文件夹下的文档时才能手动更新导航文件');
return;
}
if (editor.getPath().indexOf('library') < 0) {
alert('只有当你打开library文件夹下的文档时才能手动更新导航文件');
return;
}
this.refresh(editor.getPath(), function (libPath) {
//如果当前文库没有记录,添加记录
var hs, i;
i = 0;
hs = false;
while (i < state.libraryList.length) {
if (state.libraryList[i] === libPath) {
hs = true;
break;
}
i++;
}
if (!hs) {
state.libraryList.push(libPath);
callback && callback(libPath);
}
});
},
//刷新导航创建wiki时
refresh: function (editorPath, callback) {
var that = this;
var path = editorPath.replace(/\\/g, '/').split('library')[0] + 'library/';
callback && callback(path);
directories.readLibraryDir(path, function (err, tree, folders) {
if (err) {
console.warn(err);
} else {
that.make(path, tree);
}
});
},
//创建md导航文件
make: function (path, data) {
if (this.hasDuplicateId(data)) {
return;
}
var hsErrId = false;
var checkId = function (name, path) {
if (/^\d+(\.\d+)?[-_](.*?)$/.test(name)) {
return true
} else {
var text = 'Error File ID!\n排序id仅允许由整数或浮点数构成并使用连接符或下划线与具体名称相连\n' +
' at path "library/' + path + '"\n' +
' at file "' + name + '"';
hsErrId = true;
alert(text);
return false;
}
};
var checkFileName = function (name, path) {
if (/^\d+(\.\d+)?[-_](.*?)\.md$/.test(name)) {
return true
} else {
var errText = 'Error File Name\n文件名须由 “排序id-名称.md” 格式构成\n' +
' at path "library/' + path + '/"\n' +
' at file "' + name + '"';
alert(errText);
return false;
}
};
var markdown = '';
markdown += '\n#### [首页](?file=首页 "返回首页")\n';
for (var dir1 in data) {
if (data.hasOwnProperty(dir1) && checkId(dir1, '')) {
markdown += '\n##### ' + dir1.match(/^\d+(\.\d+)?[-_](.*?)$/)[2] + '\n';
for (var dir2 in data[dir1]) {
if (data[dir1].hasOwnProperty(dir2) && checkId(dir2, dir1 + '/')) {
//当为文件夹时
if (data[dir1][dir2]) {
markdown += '- **' + dir2.match(/^\d+(\.\d+)?[-_](.*?)$/)[2] + '**\n';
for (var dir3 in data[dir1][dir2]) {
if (data[dir1][dir2].hasOwnProperty(dir3) && checkId(dir3, dir1 + '/' + dir2 + '/')) {
if (checkFileName(dir3, dir1 + '/' + dir2 + '/')) {
var name2 = dir3.match(/^\d+(\.\d+)?[-_](.*?)\.md$/)[2];
markdown += ' - [' + name2 + '](?file=' +
dir1 + '/' + dir2 + '/' + dir3.split('.md')[0] + ' "' + name2 + '")\n';
}
}
}
}
//当为文件时
else {
if (checkFileName(dir2, dir1 + '/')) {
var name = dir2.match(/^\d+(\.\d+)?[-_](.*?)\.md$/)[2];
markdown += '- [' + name + '](?file=' +
dir1 + '/' + dir2.split('.md')[0] + ' "' + name + '")\n';
}
}
}
}
}
}
if (!hsErrId) {
fs.writeFileSync(path + '$navigation.md', markdown, 'utf-8');
}
},
//检查重复id
hasDuplicateId: function (data) {
//单层检查
var check = function (obj, path) {
var hash = {};
for (var name in obj) {
if (obj.hasOwnProperty(name)) {
if (!hash[name.split('-')[0]]) {
hash[name.split('-')[0]] = name;
} else {
alert('Duplicate File ID!\n同级目录下存在重复ID' + name.split('-')[0] + '。\n' +
' at path "library/' + path + '"\n' +
' at file "' + hash[name.split('-')[0]] + '"\n' +
' at file "' + name + '"');
return false;
}
}
}
return true;
};
//是否存在重复id
var duplicate = 'none';
//第一层library直接子级
if (check(data, '')) {
for (var p1 in data) {
if (data.hasOwnProperty(p1) && data[p1]) {
//第二层,可能是文件夹也可能是文件
if (check(data[p1], p1 + '/')) {
for (var p2 in data[p1]) {
if (data[p1].hasOwnProperty(p2) && data[p1][p2]) {
//第三层,只有文件
if (!check(data[p1][p2], p1 + '/' + p2 + '/')) {
duplicate = 'yes';
break;
}
}
}
} else {
duplicate = 'yes';
break;
}
}
}
} else {
duplicate = 'yes';
}
return duplicate == 'yes';
}
};

87
lib/pageCatalogue.js Normal file
View File

@ -0,0 +1,87 @@
/**
* 工作端 - Atom - 提取 H2-H3 为文章目录模块
* @author Tevin
*/
const pageCatalogue = (function () {
return {
/**
* 生成文章目录
* @param {Object} editor - 当前文档编辑器的引用
* @public
*/
make: function (editor) {
editor.insertText(this._extract(editor.getText()) || '');
},
/**
* 抽取标题
* @param {String} arc
* @private
*/
_extract: function (arc) {
let lines = arc.split('\n');
if (lines.length === 1) {
lines = lines[0].split('\r');
}
let contents = '',
lineStr = '',
hashStr = '',
h1 = '',
text;
//生成列表
for (let line of lines) {
line = line.replace(/^\s*/, ''); //去除行首空格
switch (line.split(/\s/)[0]) {
case '#':
text = line.split('# ')[1];
if (text !== undefined) {
h1 = this._trimTitle(text);
}
break;
case '##':
text = line.split('## ')[1];
if (text !== undefined) {
lineStr = this._trimTitle(text);
contents += '1. [' + lineStr + '](#' + lineStr.replace(/&#34;/g, '') + ' "' + lineStr + '")\n';
} else {
contents += '1. &#12288;\n';
}
break;
case '###':
text = line.split('### ')[1];
if (text !== undefined) {
lineStr = this._trimTitle(text);
contents += '\t1. [' + lineStr + '](#' + lineStr.replace(/&#34;/g, '') + ' "' + lineStr + '")\n';
} else {
contents += '\t1. &#12288;\n';
}
break;
}
}
if (contents.substr(0, 2) !== '1.') {
contents = '1. ' + h1 + '\n' + contents;
}
contents = '>' + contents;
return contents;
},
/**
* 修剪标题文本
* @param {String} titleStr
* @returns {String}
* @private
*/
_trimTitle: function (titleStr) {
return titleStr.replace(/^\s+|\s+$/g, '') //去除首尾空格
.replace(/\[(.*?)]\(.*?\)/g, '$1') //去除链接
.replace(/<.*?>/g, '') //去除html标签
.replace(/'/g, '&#39;') //转义单引号
.replace(/"/g, '&#34;') //转义双引号由于双引号无法正确传递给html属性当作为hash时将删除处理
.replace(/\(/g, '&#40;') //转义左圆括号
.replace(/\)/g, '&#41;') //转义右圆括号
.replace(/\[/g, '&#91;') //转义左中括号
.replace(/\]/g, '&#93;'); //转义右中括号
}
}
})();
module.exports = pageCatalogue;

View File

@ -1,100 +0,0 @@
/**
* @desc amWiki 工作端·粘帖图片模块
* @author Tevin
*/
var environment = require('atom'),
File = environment.File,
Directory = environment.Directory;
var fs = require("fs");
var clipboard = require('clipboard');
var crypto = require("crypto");
module.exports = {
paster: function () {
var editor = atom.workspace.getActiveTextEditor();
//状态验证当编辑md文档时才允许操作
var grammar, img;
if (!editor) {
return;
}
grammar = editor.getGrammar();
if (!grammar) {
return;
}
if (grammar.scopeName !== 'source.gfm' && grammar.scopeName !== 'text.md') {
return;
}
if (editor.getPath().substr(-3) !== '.md') {
return;
}
img = clipboard.readImage();
if (img.isEmpty()) {
return;
}
//路径计算
var assetsDirPath, //项目assets文件夹地址
creatDirPath, //本次创建图片地址
insertPath, //插入文档的图片引用路径
writePath; //图片输出路径
var mdFile = new File(editor.getPath());
//library直接子级
if (mdFile.getParent().getBaseName() === 'library') {
assetsDirPath = mdFile.getParent().getParent().getPath() + "/assets";
creatDirPath = assetsDirPath + '/';
writePath = assetsDirPath + '/';
insertPath = '';
}
//library下一级目录
else if (mdFile.getParent().getParent().getBaseName() === 'library') {
parentDir = mdFile.getParent();
assetsDirPath = parentDir.getParent().getParent().getPath() + "/assets";
creatDirPath = assetsDirPath + '/' + parentDir.getBaseName().split(/-|_/)[0];
writePath = assetsDirPath + '/' + parentDir.getBaseName().split(/-|_/)[0] + '/';
insertPath = parentDir.getBaseName().split(/-|_/)[0] + '/';
}
//library下二级目录
else if (mdFile.getParent().getParent().getParent().getBaseName() === 'library') {
parentDir = mdFile.getParent().getParent();
assetsDirPath = parentDir.getParent().getParent().getPath() + "/assets";
creatDirPath = assetsDirPath + '/' + parentDir.getBaseName().split(/-|_/)[0];
writePath = assetsDirPath + '/' + parentDir.getBaseName().split(/-|_/)[0] + '/';
insertPath = parentDir.getBaseName().split(/-|_/)[0] + '/';
}
//其他目录
else {
return;
}
//文件保存
var md5 = crypto.createHash('md5');
var imgbuffer = img.toPng();
md5.update(imgbuffer);
var filename = "" + (mdFile.getBaseName().replace(/\.\w+$/, '').replace(/\s+/g, '').split(/-|_/)[0]) +
"-" + (md5.digest('hex').slice(0, 8)) + ".png";
this.createDirectory(creatDirPath, function () {
fs.writeFile(writePath + filename, imgbuffer, 'binary', function () {
editor.insertText("![](assets/" + insertPath + filename + ")", editor);
});
});
},
createDirectory: function (dirPath, callback) {
var that = this;
var assetsDir;
assetsDir = new Directory(dirPath);
assetsDir.exists().then(function (existed) {
if (!existed) {
assetsDir.create().then(function (created) {
if (created) {
//console.log('Success Create dir');
callback();
}
});
} else {
callback();
}
});
}
};

166
lib/richPaste.js Normal file
View File

@ -0,0 +1,166 @@
/**
* 工作端 - Atom - 富粘帖操作模块
* @author Tevin
*/
const {File} = require('atom');
const fs = require('fs');
const clipboard = require('clipboard');
const crypto = require('crypto');
const mngFolder = require('../build/manageFolder');
const richPaste = (function () {
return {
/**
* 编辑器实例
* @private
*/
_editPath: null,
/**
* 富粘贴
* @param {Object} editor - 当前文档编辑器的引用
* @param {String} editPath - 当前文档的路径
* @public
*/
paste: function (editor, editPath) {
this._editPath = editPath;
//网页粘贴检查
const html = clipboard.readHtml();
if (html && html.length > 2) {
//形如 <div><img src="..."></div> 格式的 html 视为图片粘贴
const htmlMatch = html.match(/^(<div>)?\s*<img src="(.*?)" *.*?>\s*(<\/div>)?$/i);
if (htmlMatch && htmlMatch.length >= 4) {
// file 协议视为本地图片,转移图片
if (htmlMatch[2].indexOf('file:') === 0) {
this._pasteImg('file', htmlMatch[2], function (state, text, title) {
if (state) {
text = '![](' + text + title + ')' + ' \n';
editor.insertText(text, editor);
}
}, editor);
}
// http 协议视为远程图片
else if (htmlMatch[2].indexOf('http') === 0) {
//如果此时粘贴板能读取图片,则读取
this._pasteImg('clip', '', function (state, text, title) {
console.log(state, text, title);
if (state) {
text = '![](' + text + title + ')' + ' \n';
editor.insertText(text, editor);
}
//不能读取图片,则插入网络地址
else {
text = '![](' + htmlMatch[2].split(/[?#]/)[0] + title + ')' + ' \n';
editor.insertText(text, editor);
}
}, editor);
}
}
//否则视为大段 html 粘贴模式
else {
//
}
}
//截图粘贴检查
else {
this._pasteImg('clip', '', function (state, text, title) {
if (state) {
text = '![](' + text + title + ')' + ' \n';
editor.insertText(text, editor);
}
}, editor);
}
},
/**
* 粘贴图片
* @param {String} type - 粘贴的类型file / clip
* @param {String} filePath - 需要读取的图片路径
* @param {Function} callback - 粘贴处理后的回调是否成功的状态图片src路径图片title
* @param {Object} editor - 当前编辑器的引用
* @private
*/
_pasteImg: function (type, filePath, callback, editor) {
//满足一定条件的选中文本可以插入为图片 title
let selectText = editor.getSelectedText();
if (/^[a-z0-9\u4e00-\u9fa5\s\-_!,.?:;()!,。?“”:;]+$/.test(selectText)) {
selectText = ' "' + selectText.replace(/^\s+|\s+$/g, '') + '"';
} else {
selectText = '';
}
//读取数据
let imgbuffer = null;
let ext = 'png';
if (type === 'file') {
filePath = filePath.substr(8); //截去 file:/// 协议头
try {
imgbuffer = fs.readFileSync(filePath, 'binary');
ext = filePath.split(/[?#]/)[0].match(/\.([^\.\/]*?)$/);
ext = ext ? ext[1] : 'png';
} catch (e) {
callback && callback(false);
return;
}
} else if (type === 'clip') {
const img = clipboard.readImage();
if (img.isEmpty()) {
callback && callback(false);
return;
}
imgbuffer = img.toPng();
}
//计算路径
const [assetsDirPath, createDirPath, insertText] = this.getPastePaths();
if (!assetsDirPath) {
callback && callback(false);
return;
}
//计算文件名
let filename = '';
//按天区分文件
const date = new Date();
let month = date.getMonth() + 1;
let day = date.getDate();
month = (month <= 9 ? '0' : '') + month;
day = (day <= 9 ? '0' : '') + day;
//加入 md5 创建文件名,重复多次粘贴时只创建一个图片文件
const md5 = crypto.createHash('md5');
md5.update(imgbuffer);
filename += date.getFullYear() + month + day + '-' + md5.digest('hex').slice(0, 8) + '.' + ext;
//输出文件
mngFolder.createFolder(createDirPath);
fs.writeFile(createDirPath + filename, imgbuffer, 'binary', () => {
callback && callback(true, insertText + filename, selectText);
});
},
/**
* 计算操作路径
* @param {String} [editPath] - 当前文档的路径
* @returns {Object} 粘贴图片需要的路径列表
* @public
*/
getPastePaths: function (editPath = this._editPath) {
const path = mngFolder.getProjectFolder(editPath);
if (!path) {
return [];
}
let assetsDirPath = path + 'assets/'; //项目assets文件夹地址;
let createDirPath = '', //本次创建图片地址
insertText = ''; //插入文档的图片引用路径
// library 目录
if (mngFolder.getBaseName(mngFolder.getParentFolder(editPath)) === 'library') {
createDirPath = assetsDirPath;
insertText = 'assets/'
}
// library 深子级
else {
let lv1Name = editPath.substr((path + 'library/').length);
let lv1Id = lv1Name.split(/[-_]/)[0];
createDirPath = assetsDirPath + lv1Id + '/';
insertText = 'assets/' + lv1Id + '/';
}
return [assetsDirPath, createDirPath, insertText];
}
};
})();
module.exports = richPaste;

View File

@ -1,221 +0,0 @@
/**
* @desc amWiki 工作端·本地服务器模块
* @author Tevin
*/
var http = require('http');
var url = require('url');
var fs = require('fs');
var util = require('util');
var os = require('os');
var child_process = require("child_process");
var GBK = require('../files/gbk.js').GBK;
//文件类型
var mimeType = {
'txt': 'text/plain',
'html': 'text/html',
'css': 'text/css',
'xml': 'application/xml',
'json': 'application/json',
'js': 'application/javascript',
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'gif': 'image/gif',
'png': 'image/png',
'svg': 'image/svg+xml',
"swf": "application/x-shockwave-flash",
"tiff": "image/tiff",
"wav": "audio/x-wav",
"wma": "audio/x-ms-wma",
"wmv": "video/x-ms-wmv"
};
//404未找到页面
var page_404 = function (req, res, path) {
res.writeHead(404, {
'Content-Type': 'text/html'
});
res.write('<!doctype html>\n');
res.write('<title>404 Not Found</title>\n');
res.write('<h1>Not Found</h1>');
res.write(
'<p>The requested URL ' +
path +
' was not found on this server.</p>'
);
res.end();
};
//500错误页面
var page_500 = function (req, res, error) {
res.writeHead(500, {
'Content-Type': 'text/html'
});
res.write('<!doctype html>\n');
res.write('<title>Internal Server Error</title>\n');
res.write('<h1>Internal Server Error</h1>');
res.write('<pre>' + util.inspect(error) + '</pre>');
};
//获取本地ip
function getLocalIP() {
var ifaces = os.networkInterfaces();
var ip = '';
for (var dev in ifaces) {
if (ifaces.hasOwnProperty(dev)) {
ifaces[dev].forEach(function (details) {
if (details.family == 'IPv4' && details.address.indexOf(192) >= 0) {
ip = details.address;
}
});
}
}
if (ip == '') {
ip = '127.0.0.1';
}
return ip;
}
module.exports = {
//web服务器
server: null,
//地址映射列表
mappingList: {},
//创建web服务器
createServer: function () {
var that = this;
this.server = http.createServer(function (req, res) {
var pathname = url.parse(req.url).pathname;
var mappingId = '';
var filePath = pathname.replace(/^\/wiki(\d{3,}?)\//g, function (match, $1) {
mappingId = 'wiki' + $1;
return ''
});
if (!that.mappingList[mappingId]) {
return page_404(req, res, pathname);
}
//编码切换
try {
filePath = decodeURI(filePath);
} catch (e) {
filePath = GBK.decode(filePath);
}
//真实地址
var realPath = that.mappingList[mappingId] + filePath;
//解析文件
fs.exists(realPath, function (exists) {
if (!exists) {
return page_404(req, res, pathname);
} else {
var file = fs.createReadStream(realPath);
res.writeHead(200, {
'Content-Type': mimeType[realPath.split('.').pop()] || 'text/plain'
});
file.on('data', res.write.bind(res));
file.on('close', res.end.bind(res));
file.on('error', function (err) {
return page_500(req, res, err);
});
}
});
}).listen(5171);
console.info('Server running at http://' + getLocalIP() + ':5171/');
},
//更新映射列表
updateMap: function(list) {
this.mappingList = {};
for (var i = 0; i < list.length; i++) {
var path = list[i].replace('library/', '');
//缩短数字并设置为地址映射名
this.mappingList['wiki' + this.createMappingId(path)] = path;
}
},
//启动服务器
run: function (list) {
this.updateMap(list);
if (!this.server) {
this.createServer();
}
},
//创建映射列表
createMappingId: function (path) {
path = path.replace(/\\/g, '/');
//累加地址字符串每个字符Unicode值与其序号的乘积
var code = 0;
for (var j = 0; j < path.length; j++) {
code += path.charCodeAt(j) * j;
}
//再与地址字符串长度拼合
code = parseInt(path.length + '' + code);
return code;
},
//浏览当前文档
browser: function (list) {
//编辑器
var editor = atom.workspace.getActiveTextEditor();
//状态验证当编辑md文档时才允许操作
var grammar, img;
if (!editor) {
return;
}
grammar = editor.getGrammar();
if (!grammar) {
return;
}
if (grammar.scopeName !== 'source.gfm' && grammar.scopeName !== 'text.md') {
return;
}
if (editor.getPath().substr(-3) !== '.md') {
alert('请先打开一篇文档!');
return;
}
//更新地址映射
for (var i = 0; i < list.length; i++) {
var path = list[i].replace('library/', '');
//缩短数字并设置为地址映射名
this.mappingList['wiki' + this.createMappingId(path)] = path;
}
//判断服务器
if (!this.server) {
if (confirm('本地服务器还未启动,您需要启动服务器么?')) {
this.createServer();
}
}
//解析地址
var host = 'http://' + getLocalIP() + ':5171';
var editorPath = editor.getPath();
var mappingId = this.createMappingId(editorPath.split('library')[0]);
var url;
if (editorPath.indexOf('$navigation.md') >= 0) {
url = host + '/wiki' + mappingId + '/index.html';
} else {
var filePath = editorPath.split('library\\')[1];
if (typeof filePath == 'undefined') {
url = host + '/wiki' + mappingId + '/index.html';
} else {
filePath = filePath.replace(/\\/g, '/').replace('.md', '');
url = host + '/wiki' + mappingId + '/index.html?file=' + filePath;
}
}
//呼起默认浏览器打开页面
var cmd;
//windows
if (process.platform == 'win32') {
cmd = 'start';
}
//linux
else if (process.platform == 'linux') {
cmd = 'xdg-open';
}
//mac
else if (process.platform == 'darwin') {
cmd = 'open';
}
child_process.exec(cmd + ' ' + url);
},
//关闭服务器
destroy: function () {
this.server && this.server.close();
}
};

View File

@ -1,21 +1,39 @@
'menu': [{
'label': 'amWiki轻文库',
'submenu': [{
'label': '通过 “config.json” 创建新文库',
'label': '通过 "config.json" 创建新文库',
'command': 'amWiki:create'
}, {
'type': 'separator'
}, {
'label': '提取 h2,h3 标题为篇内目录',
'command': 'amWiki:catalogue'
}, {
'label': '从剪切板粘贴截图',
'command': 'amWiki:paste'
}, {
'label': '手动更新当前文库导航文件',
'command': 'amWiki:makeNav'
}, {
'label': '提取h2、h3标题为页内目录',
'command': 'amWiki:catalogue'
'label': '手动更新当前文库页面挂载数据',
'command': 'amWiki:makeMut'
}, {
'type': 'separator'
}, {
'label': '启动本地静态web服务器',
'label': '启动本地静态 Web 服务器',
'command': 'amWiki:runServer'
}, {
'label': '在浏览器打开当前文档',
'command': 'amWiki:browser'
}, {
'type': 'separator'
}, {
'label': '导出当前文库为 Github Wiki',
'command': 'amWiki:export.gitHub'
}, {
'type': 'separator'
}, {
'label': '关于 amWiki',
'command': 'amWiki:about'
}]
}]

239
modules/co.js Normal file
View File

@ -0,0 +1,239 @@
/**
* slice() reference.
*/
var slice = Array.prototype.slice;
/**
* Expose `co`.
*/
module.exports = co['default'] = co.co = co;
/**
* Wrap the given generator `fn` into a
* function that returns a promise.
* This is a separate function so that
* every `co()` call doesn't create a new,
* unnecessary closure.
*
* @param {GeneratorFunction} fn
* @return {Function}
* @api public
*/
co.wrap = function (fn) {
createPromise.__generatorFunction__ = fn;
return createPromise;
function createPromise() {
return co.call(this, fn.apply(this, arguments));
}
};
/**
* Execute the generator function or a generator
* and return a promise.
*
* @param {Function} fn
* @return {Promise}
* @api public
*/
function co(gen) {
var ctx = this;
var args = slice.call(arguments, 1);
// we wrap everything in a promise to avoid promise chaining,
// which leads to memory leak errors.
// see https://github.com/tj/co/issues/180
return new Promise(function(resolve, reject) {
if (typeof gen === 'function') gen = gen.apply(ctx, args);
if (!gen || typeof gen.next !== 'function') return resolve(gen);
onFulfilled();
/**
* @param {Mixed} res
* @return {Promise}
* @api private
*/
function onFulfilled(res) {
var ret;
try {
ret = gen.next(res);
} catch (e) {
return reject(e);
}
next(ret);
return null;
}
/**
* @param {Error} err
* @return {Promise}
* @api private
*/
function onRejected(err) {
var ret;
try {
ret = gen.throw(err);
} catch (e) {
return reject(e);
}
next(ret);
}
/**
* Get the next value in the generator,
* return a promise.
*
* @param {Object} ret
* @return {Promise}
* @api private
*/
function next(ret) {
if (ret.done) return resolve(ret.value);
var value = toPromise.call(ctx, ret.value);
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
+ 'but the following object was passed: "' + String(ret.value) + '"'));
}
});
}
/**
* Convert a `yield`ed value into a promise.
*
* @param {Mixed} obj
* @return {Promise}
* @api private
*/
function toPromise(obj) {
if (!obj) return obj;
if (isPromise(obj)) return obj;
if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj);
if ('function' == typeof obj) return thunkToPromise.call(this, obj);
if (Array.isArray(obj)) return arrayToPromise.call(this, obj);
if (isObject(obj)) return objectToPromise.call(this, obj);
return obj;
}
/**
* Convert a thunk to a promise.
*
* @param {Function}
* @return {Promise}
* @api private
*/
function thunkToPromise(fn) {
var ctx = this;
return new Promise(function (resolve, reject) {
fn.call(ctx, function (err, res) {
if (err) return reject(err);
if (arguments.length > 2) res = slice.call(arguments, 1);
resolve(res);
});
});
}
/**
* Convert an array of "yieldables" to a promise.
* Uses `Promise.all()` internally.
*
* @param {Array} obj
* @return {Promise}
* @api private
*/
function arrayToPromise(obj) {
return Promise.all(obj.map(toPromise, this));
}
/**
* Convert an object of "yieldables" to a promise.
* Uses `Promise.all()` internally.
*
* @param {Object} obj
* @return {Promise}
* @api private
*/
function objectToPromise(obj){
var results = new obj.constructor();
var keys = Object.keys(obj);
var promises = [];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var promise = toPromise.call(this, obj[key]);
if (promise && isPromise(promise)) defer(promise, key);
else results[key] = obj[key];
}
return Promise.all(promises).then(function () {
return results;
});
function defer(promise, key) {
// predefine the key in the result
results[key] = undefined;
promises.push(promise.then(function (res) {
results[key] = res;
}));
}
}
/**
* Check if `obj` is a promise.
*
* @param {Object} obj
* @return {Boolean}
* @api private
*/
function isPromise(obj) {
return 'function' == typeof obj.then;
}
/**
* Check if `obj` is a generator.
*
* @param {Mixed} obj
* @return {Boolean}
* @api private
*/
function isGenerator(obj) {
return 'function' == typeof obj.next && 'function' == typeof obj.throw;
}
/**
* Check if `obj` is a generator function.
*
* @param {Mixed} obj
* @return {Boolean}
* @api private
*/
function isGeneratorFunction(obj) {
var constructor = obj.constructor;
if (!constructor) return false;
if ('GeneratorFunction' === constructor.name || 'GeneratorFunction' === constructor.displayName) return true;
return isGenerator(constructor.prototype);
}
/**
* Check for plain object.
*
* @param {Mixed} val
* @return {Boolean}
* @api private
*/
function isObject(val) {
return Object == val.constructor;
}

View File

@ -1,8 +1,9 @@
{
"name": "amWiki",
"main": "./lib/main",
"version": "0.7.3",
"description": "amWiki是一套非常简单基于atom编辑器markdown语法的轻量级wiki文库系统仅仅需要您使用Atom编辑markdown文档和拥有http纯静态访问空间使用amWiki您无需服务端开发、不用数据库、一键创建新文库、自动更新文库目录、支持截图直接粘帖为本地png并插入当前markdown...",
"main": "./lib/main.js",
"bin": "./bin/main.js",
"version": "1.2.1",
"description": "amWiki 是一款由 JS 开发、依赖 Atom 或 Nodejs-Npm 的 Markdown 轻量级前端化开源文库系统。amWiki 致力于让大家可以更简单、更便捷的建设个人和团队文库!",
"keywords": [
"amWiki",
"markdown",
@ -16,12 +17,13 @@
},
"license": "MIT",
"engines": {
"atom": ">=1.0.0 <2.0.0"
"atom": ">=1.0.0 <2.0.0",
"node": ">=1.0.0"
},
"bugs": {
"url": "https://github.com/TevinLi/amWiki/issues"
},
"homepage": "https://github.com/TevinLi/amWiki",
"homepage": "http://amwiki.org",
"configSchema": {
"checkLibraryOnWindowFocus": {
"type": "boolean",