Compare commits

..

No commits in common. "master" and "v5.15.15" have entirely different histories.

120 changed files with 764 additions and 5309 deletions

View File

@ -4,6 +4,3 @@ dist
.history .history
images images
docs docs
Dockerfile
README.md
build.sh

View File

@ -1,5 +1,6 @@
FROM mafgwo/wkhtmltopdf-nodejs:11.15.0 FROM mafgwo/wkhtmltopdf-nodejs:11.15.0
RUN mkdir -p /opt/stackedit
WORKDIR /opt/stackedit WORKDIR /opt/stackedit
COPY package*json /opt/stackedit/ COPY package*json /opt/stackedit/

View File

@ -5,7 +5,7 @@ StackEdit中文版
</h1> </h1>
<p align="center"> <p align="center">
<strong>笔记利器在线Markdown编辑器。</strong><br> <strong>笔记利器在线Markdown编辑器。</strong><br>
项目clone自<a href="https://gitee.com/mafgwo/stackedit" target="_blank" title="豆萁">豆萁/stackedit</a>如果你喜欢该项目,请过去点一下Star您的肯定是作者最大的动力 如果你喜欢该项目请点一下Star您的肯定是作者最大的动力
</p> </p>
<p align="center"> <p align="center">
<a href="https://stackedit.cn/">https://stackedit.cn</a> <a href="https://stackedit.cn/">https://stackedit.cn</a>
@ -22,38 +22,36 @@ StackEdit中文版
</a> </a>
</p> </p>
<br/> <br/>
<p align="center">
<a target="_blank" href="https://jq.qq.com/?_wv=1027&k=wUSCNqmN">
<img src="https://img.shields.io/badge/QQ交流群-703187410-orange"/></a>
</p>
<hr /> <hr />
1 笔记支持Gitee、GitHub、Gitea等Git仓库存储。<br> 1 笔记支持Gitee、GitHub、Gitea等Git仓库存储。<br>
2 支持直接上传图片也支持多种外部图床GitHub、Gitea、SM.MS、自定义图床粘贴或拖拽上传。<br> 2 支持多种图床GitHub、Gitea、SM.MS、自定义图床粘贴或拖拽上传。<br>
3 编辑区域支持选择主题或自定义,总有你喜欢的主题。<br> 3 编辑区域支持选择主题或自定义,总有你喜欢的主题。<br>
4 支持历史版本管理,不用担心编辑覆盖后无法回滚。<br> 4 支持历史版本管理,不用担心编辑覆盖后无法回滚。<br>
5 支持ChatGPT辅助写作。<br> 5 支持KaTeX数学表达式、Mermaid UML图、乐谱等扩展。
6 支持KaTeX数学表达式、Mermaid UML图、乐谱等扩展。
<hr /> <hr />
## 说明
本项目为本人clone修改自用如果你也喜欢请至原作者处获取及交流。
## 截图 ## 截图
**亮暗主题切换、编辑主题切换** **示例截图-暗色主题**
![](./images/theme.gif) ![](./images/dark.png)
**支持的文档空间** **示例截图-亮色主题**
![](./images/light.png)
**示例截图-支持的文档空间**
![](./images/workspace.png) ![](./images/workspace.png)
**拖拽粘贴上传图片** **示例截图-支持的图床**
![](./images/uploadimg.gif) ![](./images/imageBed.png)
**支持文档搜索** **示例截图-支持文件搜索**
![](./images/search.gif) ![](./images/fileSearch.png)
**ChatGPT集成协助写作**
![](./images/chatgpt.gif)
## 相比国外开源版本的区别: ## 相比国外开源版本的区别:
- 修复了Github授权登录问题 - 修复了Github授权登录问题
- 支持了Gitee仓库2022-05-25 - 支持了Gitee仓库2022-05-25
- 支持了Gitea仓库2022-05-25 - 支持了Gitea仓库2022-05-25
@ -72,27 +70,16 @@ StackEdit中文版
- Gitea支持后端配置指定应用ID和Secret2022-10-03 - Gitea支持后端配置指定应用ID和Secret2022-10-03
- 支持编辑区域选择主题样式2022-10-06 - 支持编辑区域选择主题样式2022-10-06
- 支持图片直接存储到当前文档空间2022-10-29 - 支持图片直接存储到当前文档空间2022-10-29
- 支持MD文档之间链接跳转2022-11-20
- 支持预览区域选择主题样式2022-12-04
- Gitlab的支持优化2023-02-23
- 导出HTML、PDF支持带预览主题导出2023-02-26
- 支持分享文档2023-03-30
- 支持ChatGPT生成内容2023-04-10
- GitLab授权接口调整2023-08-26
- 主文档空间支持GitHub登录2023-10-19
## 国外开源版本弊端: ## 国外开源版本弊端:
- 作者已经不维护了
- 作者已经不维护了或很少维护了 - Github授权登录存在问题
- 不支持国内常用Gitee - 不支持国内常用Gitee
- 强依赖GoogleDrive而Google Drive在国内不能正常访问 - 强依赖GoogleDrive而Google Drive在国内不能正常访问
## 部署说明 ## 部署说明
> 建议docker-compose方式部署其他部署方式如遇到问题欢迎提issue。 > 建议docker-compose方式部署其他部署方式如遇到问题欢迎提issue。
docker官方仓库下载太慢可以使用阿里云的镜像仓库镜像仓库地址registry.cn-hangzhou.aliyuncs.com/mafgwo/stackedit:【版本号】
`docker-compose.yml`如下: `docker-compose.yml`如下:
```yaml ```yaml
@ -116,9 +103,6 @@ services:
- GITEA_CLIENT_ID=【不需要支持则删掉】 - GITEA_CLIENT_ID=【不需要支持则删掉】
- GITEA_CLIENT_SECRET=【不需要支持则删掉】 - GITEA_CLIENT_SECRET=【不需要支持则删掉】
- GITEA_URL=【不需要支持则删掉】 - GITEA_URL=【不需要支持则删掉】
- GITLAB_CLIENT_ID=【不需要支持则删掉】
- GITLAB_CLIENT_SECRET=【不需要支持则删掉】
- GITLAB_URL=【不需要支持则删掉】
ports: ports:
- 8080:8080/tcp - 8080:8080/tcp
network_mode: bridge network_mode: bridge
@ -126,7 +110,6 @@ services:
``` ```
docker-compose方式的启动或停止命令 docker-compose方式的启动或停止命令
```bash ```bash
# 在 docker-compose.yml 文件目录下 启动命令 # 在 docker-compose.yml 文件目录下 启动命令
docker-compose up -d docker-compose up -d
@ -154,26 +137,21 @@ docker run -itd --name stackedit \
-e GITEA_CLIENT_ID=【不需要支持则删掉】 \ -e GITEA_CLIENT_ID=【不需要支持则删掉】 \
-e GITEA_CLIENT_SECRET=【不需要支持则删掉】 \ -e GITEA_CLIENT_SECRET=【不需要支持则删掉】 \
-e GITEA_URL=【不需要支持则删掉】 \ -e GITEA_URL=【不需要支持则删掉】 \
-e GITLAB_CLIENT_ID=【不需要支持则删掉】 \
-e GITLAB_CLIENT_SECRET=【不需要支持则删掉】 \
-e GITLAB_URL=【不需要支持则删掉】 \
mafgwo/stackedit:【docker中央仓库找到最新版本】 mafgwo/stackedit:【docker中央仓库找到最新版本】
``` ```
## 如何创建三方平台应用 ## 如何创建三方平台应用
> 部署时如果需要支持Gitee或GitHub则需要自行到对应三方平台创建应用获取到应用ID和秘钥替换到以上的环境变量中再启动应用。 > 部署时如果需要支持Gitee或GitHub则需要自行到对应三方平台创建应用获取到应用ID和秘钥替换到以上的环境变量中再启动应用。
- Gitee的环境变量GITEE_CLIENT_ID、GITEE_CLIENT_SECRET**[如何创建Gitee应用](./docs/部署之Gitee应用创建.md)** - Gitee的环境变量GITEE_CLIENT_ID、GITEE_CLIENT_SECRET**[如何创建Gitee应用](./docs/部署之Gitee应用创建.md)**
- GitHub的环境变量GITHUB_CLIENT_ID、GITEE_CLIENT_SECRET**[如何创建GitHub应用](./docs/部署之GitHub应用创建.md)**
- Gitea可选择性配置环境变量未配置则在关联时前端指定有配置则仅允许配置的应用信息GITEA_CLIENT_ID、GITEA_CLIENT_SECRET、GITEA_URL**[如何创建Gitea应用](./docs/部署之Gitea应用创建.md)**
- Gitlab可选择性配置环境变量未配置则在关联时前端指定有配置则仅允许配置的应用信息GITLAB_CLIENT_ID、GITLAB_CLIENT_SECRET、GITLAB_URL **如何创建Gitlab应用(待补充文档)**
特别说明自建的Gitea、Gitlab要能接入stackedit必须支持跨域 - GitHub的环境变量GITHUB_CLIENT_ID、GITEE_CLIENT_SECRET**[如何创建GitHub应用](./docs/部署之GitHub应用创建.md)**
- Gitea可选择性配置环境变量未配置则在关联时前端指定有配置则仅允许配置的应用信息GITEA_CLIENT_ID、GITEA_CLIENT_SECRET、GITEA_URL**[如何创建Gitea应用](./docs/部署之Gitea应用创建.md)**
## 编译与运行 ## 编译与运行
> 编译运行的nodejs版本选择11.15.0版本 > 编译运行的nodejs版本选择11.15.0版本
```bash ```bash
@ -189,3 +167,7 @@ npm run build
# build for production and view the bundle analyzer report # build for production and view the bundle analyzer report
npm run build --report npm run build --report
``` ```
## 欢迎加群交流
关于StackEdit如果你有想法或者使用中遇到了问题可以提Issue如果需要快速得到反馈可以加QQ群如下加群后可直接@群主):
![](./images/qq.jpeg)

View File

@ -1,37 +0,0 @@
#!/bin/bash
# 检查参数是否提供版本号
if [ -z "$1" ]; then
echo "请提供版本号作为参数"
exit 1
fi
# 定义版本号变量
VERSION="$1"
IMAGE_NAME="mafgwo/stackedit"
# 构建 Docker 镜像
build_image() {
docker build -t "$IMAGE_NAME" .
}
# 标记 Docker 镜像
tag_image() {
docker tag "$IMAGE_NAME" "$IMAGE_NAME:$VERSION"
docker tag "$IMAGE_NAME" "registry.cn-hangzhou.aliyuncs.com/$IMAGE_NAME:$VERSION"
}
# 推送 Docker 镜像
push_image() {
docker push "$IMAGE_NAME"
docker push "registry.cn-hangzhou.aliyuncs.com/$IMAGE_NAME"
docker push "$IMAGE_NAME:$VERSION"
docker push "registry.cn-hangzhou.aliyuncs.com/$IMAGE_NAME:$VERSION"
}
# 执行构建、标记和推送
build_image
tag_image
push_image
echo "操作完成"

View File

@ -18,8 +18,6 @@ awsSecretAccessKey: ""
giteaClientId: "" giteaClientId: ""
giteaClientSecret: "" giteaClientSecret: ""
giteaUrl: "" giteaUrl: ""
gitlabClientId: ""
gitlabUrl: ""
replicaCount: 1 replicaCount: 1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 960 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -1,7 +1,7 @@
{ {
"name": "StackEdit中文版", "name": "StackEdit中文版",
"description": "支持Gitee仓库/粘贴图片自动上传的浏览器内 Markdown 编辑器", "description": "支持Gitee仓库/粘贴图片自动上传的浏览器内 Markdown 编辑器",
"version": "5.15.17", "version": "5.15.15",
"manifest_version": 2, "manifest_version": 2,
"container" : "GITEE", "container" : "GITEE",
"api_console_project_id" : "241271498917", "api_console_project_id" : "241271498917",
@ -15,10 +15,10 @@
}, },
"app": { "app": {
"urls": [ "urls": [
"https://md.jonylee.top/" "https://stackedit.cn/"
], ],
"launch": { "launch": {
"web_url": "https://md.jonylee.top/app" "web_url": "https://stackedit.cn/app"
} }
}, },
"offline_enabled": true, "offline_enabled": true,

View File

@ -12,7 +12,4 @@ module.exports = merge(prodEnv, {
// GITEA_CLIENT_ID: '"fe30f8f9-b1e8-4531-8f72-c1a5d3912805"', // GITEA_CLIENT_ID: '"fe30f8f9-b1e8-4531-8f72-c1a5d3912805"',
// GITEA_CLIENT_SECRET: '"lus7oMnb3H6M1hsChndphArE20Txr7erwJLf7SDBQWTw"', // GITEA_CLIENT_SECRET: '"lus7oMnb3H6M1hsChndphArE20Txr7erwJLf7SDBQWTw"',
// GITEA_URL: '"https://gitea.test.com"', // GITEA_URL: '"https://gitea.test.com"',
GITLAB_CLIENT_ID: '"074cd5103c62dea0f479dac861039656ac80935e304c8113a02cc64c629496ae"',
GITLAB_CLIENT_SECRET: '"6f406f24216b686d55d28313dec1913c2a8e599afdb08380d5e8ce838e16e41e"',
GITLAB_URL: '"http://gitlab.qicoder.com"',
}) })

View File

@ -1,10 +0,0 @@
# 大文档导出PDF方式说明
> 由于大文档导出PDF需要消费非常多的服务器资源而且很容易导致导出超时故导出PDF的MD文档过大时可以使用 **[wkhtmltopdf](https://wkhtmltopdf.org/downloads.html)** 工具导出。
# 操作步骤
- 先在 **[StackEdit中文版](https://stackedit.cn/app)** 中使用 `导出为HTML` 功能导出MD文档导出后可以得到一个HTML文档。
- 到 **[wkhtmltopdf](https://wkhtmltopdf.org/downloads.html)** 官网下载安装程序。
- 使用 wkhtmltopdf 的导出PDF的命令 `wkhtmltopdf [GLOBAL OPTION]... [OBJECT]... <output file>` 把HTML导出为PDF如简单的导出命令`wkhtmltopdf test.html test.pdf`,具体的 `GLOBAL OPTION` 参数说明可以通过 `wkhtmltopdf -H` 查看帮助文档。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 360 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 937 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 761 KiB

View File

@ -1,31 +1,28 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Markdown编辑器-JonyLee的设计导航</title> <title>StackEdit中文版</title>
<link rel="canonical" href="https://md.jonylee.top"> <link rel="canonical" href="https://stackedit.cn/app">
<meta name="description" content="StackEdit中文版免费开源功能全面的Markdown编辑器。"> <meta name="description" content="免费开源功能全面的Markdown编辑器。">
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-status-bar-style" content="black">
<script>
var _hmt = _hmt || [];
</script>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
<!-- built files will be auto injected --> <!-- built files will be auto injected -->
<!-- baidu统计 -->
<script> <script>
var _hmt = _hmt || [];
(function() { (function() {
var hm = document.createElement("script"); var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?dad4b4383b13eedea1ab45ee323df1c3"; hm.src = "https://hm.baidu.com/hm.js?20a1e7a201b42702c49074c87a1f1035";
var s = document.getElementsByTagName("script")[0]; var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s); s.parentNode.insertBefore(hm, s);
})(); })();
</script> </script>
<!-- baidu统计结束 -->
</body> </body>
</html> </html>

223
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "stackedit", "name": "stackedit",
"version": "5.15.21", "version": "5.15.15",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -736,8 +736,7 @@
"abab": { "abab": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz",
"integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==", "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w=="
"dev": true
}, },
"abbrev": { "abbrev": {
"version": "1.1.1", "version": "1.1.1",
@ -764,8 +763,7 @@
"acorn": { "acorn": {
"version": "5.3.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.3.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.3.0.tgz",
"integrity": "sha512-Yej+zOJ1Dm/IMZzzj78OntP/r3zHEaKcyNoU2lAaxPtrseM6rF0xwqoz5Q5ysAiED9hTjI2hgtvLXitlCN1/Ug==", "integrity": "sha512-Yej+zOJ1Dm/IMZzzj78OntP/r3zHEaKcyNoU2lAaxPtrseM6rF0xwqoz5Q5ysAiED9hTjI2hgtvLXitlCN1/Ug=="
"dev": true
}, },
"acorn-dynamic-import": { "acorn-dynamic-import": {
"version": "2.0.2", "version": "2.0.2",
@ -788,7 +786,6 @@
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.1.0.tgz", "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.1.0.tgz",
"integrity": "sha512-KjZwU26uG3u6eZcfGbTULzFcsoz6pegNKtHPksZPOUsiKo5bUmiBPa38FuHZ/Eun+XYh/JCCkS9AS3Lu4McQOQ==", "integrity": "sha512-KjZwU26uG3u6eZcfGbTULzFcsoz6pegNKtHPksZPOUsiKo5bUmiBPa38FuHZ/Eun+XYh/JCCkS9AS3Lu4McQOQ==",
"dev": true,
"requires": { "requires": {
"acorn": "^5.0.0" "acorn": "^5.0.0"
} }
@ -1024,8 +1021,7 @@
"array-equal": { "array-equal": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
"integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM="
"dev": true
}, },
"array-filter": { "array-filter": {
"version": "0.0.1", "version": "0.0.1",
@ -1241,8 +1237,7 @@
"async-limiter": { "async-limiter": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
"dev": true
}, },
"async-settle": { "async-settle": {
"version": "1.0.0", "version": "1.0.0",
@ -1278,6 +1273,38 @@
"postcss-value-parser": "^3.2.3" "postcss-value-parser": "^3.2.3"
} }
}, },
"aws-sdk": {
"version": "2.317.0",
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.317.0.tgz",
"integrity": "sha512-X2Cd1Gb9Cf9WVgGOiBSW4TK6q5Mb6AYiGmEA9XikCgur4H8E4TgmgWbBWJnTzxssugclVLVoWQfw3RshNKJksg==",
"requires": {
"buffer": "4.9.1",
"events": "1.1.1",
"ieee754": "1.1.8",
"jmespath": "0.15.0",
"querystring": "0.2.0",
"sax": "1.2.1",
"url": "0.10.3",
"uuid": "3.1.0",
"xml2js": "0.4.19"
},
"dependencies": {
"xml2js": {
"version": "0.4.19",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
"integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
"requires": {
"sax": ">=0.6.0",
"xmlbuilder": "~9.0.1"
}
},
"xmlbuilder": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
"integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
}
}
},
"aws-sign2": { "aws-sign2": {
"version": "0.7.0", "version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
@ -2319,8 +2346,7 @@
"base64-js": { "base64-js": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz",
"integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==", "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw=="
"dev": true
}, },
"bcrypt-pbkdf": { "bcrypt-pbkdf": {
"version": "1.0.2", "version": "1.0.2",
@ -2455,8 +2481,7 @@
"browser-process-hrtime": { "browser-process-hrtime": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz",
"integrity": "sha1-Ql1opY00R/AqBKqJQYf86K+Le44=", "integrity": "sha1-Ql1opY00R/AqBKqJQYf86K+Le44="
"dev": true
}, },
"browser-resolve": { "browser-resolve": {
"version": "1.11.3", "version": "1.11.3",
@ -2568,7 +2593,6 @@
"version": "4.9.1", "version": "4.9.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
"dev": true,
"requires": { "requires": {
"base64-js": "^1.0.2", "base64-js": "^1.0.2",
"ieee754": "^1.1.4", "ieee754": "^1.1.4",
@ -3880,14 +3904,12 @@
"cssom": { "cssom": {
"version": "0.3.4", "version": "0.3.4",
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.4.tgz", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.4.tgz",
"integrity": "sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog==", "integrity": "sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog=="
"dev": true
}, },
"cssstyle": { "cssstyle": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.1.1.tgz", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.1.1.tgz",
"integrity": "sha512-364AI1l/M5TYcFH83JnOH/pSqgaNnKmYgKrm0didZMGKWjQB60dymwWy1rKUgL3J1ffdq9xVi2yGLHdSjjSNog==", "integrity": "sha512-364AI1l/M5TYcFH83JnOH/pSqgaNnKmYgKrm0didZMGKWjQB60dymwWy1rKUgL3J1ffdq9xVi2yGLHdSjjSNog==",
"dev": true,
"requires": { "requires": {
"cssom": "0.3.x" "cssom": "0.3.x"
} }
@ -3988,7 +4010,7 @@
}, },
"d3-collection": { "d3-collection": {
"version": "1.0.7", "version": "1.0.7",
"resolved": "https://registry.npmmirror.com/d3-collection/-/d3-collection-1.0.7.tgz", "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz",
"integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A=="
}, },
"d3-color": { "d3-color": {
@ -4166,7 +4188,7 @@
}, },
"d3-voronoi": { "d3-voronoi": {
"version": "1.1.4", "version": "1.1.4",
"resolved": "https://registry.npmmirror.com/d3-voronoi/-/d3-voronoi-1.1.4.tgz", "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz",
"integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==" "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg=="
}, },
"d3-zoom": { "d3-zoom": {
@ -4183,7 +4205,7 @@
}, },
"dagre": { "dagre": {
"version": "0.8.5", "version": "0.8.5",
"resolved": "https://registry.npmmirror.com/dagre/-/dagre-0.8.5.tgz", "resolved": "https://registry.npmjs.org/dagre/-/dagre-0.8.5.tgz",
"integrity": "sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==", "integrity": "sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==",
"requires": { "requires": {
"graphlib": "^2.1.8", "graphlib": "^2.1.8",
@ -4192,14 +4214,14 @@
"dependencies": { "dependencies": {
"lodash": { "lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
} }
} }
}, },
"dagre-d3": { "dagre-d3": {
"version": "0.6.4", "version": "0.6.4",
"resolved": "https://registry.npmmirror.com/dagre-d3/-/dagre-d3-0.6.4.tgz", "resolved": "https://registry.npmjs.org/dagre-d3/-/dagre-d3-0.6.4.tgz",
"integrity": "sha512-e/6jXeCP7/ptlAM48clmX4xTZc5Ek6T6kagS7Oz2HrYSdqcLZFLqpAfh7ldbZRFfxCZVyh61NEPR08UQRVxJzQ==", "integrity": "sha512-e/6jXeCP7/ptlAM48clmX4xTZc5Ek6T6kagS7Oz2HrYSdqcLZFLqpAfh7ldbZRFfxCZVyh61NEPR08UQRVxJzQ==",
"requires": { "requires": {
"d3": "^5.14", "d3": "^5.14",
@ -4210,7 +4232,7 @@
"dependencies": { "dependencies": {
"lodash": { "lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
} }
} }
@ -4227,7 +4249,6 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.0.1.tgz", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.0.1.tgz",
"integrity": "sha512-0HdcMZzK6ubMUnsMmQmG0AcLQPvbvb47R0+7CCZQCYgcd8OUWG91CG7sM6GoXgjz+WLl4ArFzHtBMy/QqSF4eg==", "integrity": "sha512-0HdcMZzK6ubMUnsMmQmG0AcLQPvbvb47R0+7CCZQCYgcd8OUWG91CG7sM6GoXgjz+WLl4ArFzHtBMy/QqSF4eg==",
"dev": true,
"requires": { "requires": {
"abab": "^2.0.0", "abab": "^2.0.0",
"whatwg-mimetype": "^2.1.0", "whatwg-mimetype": "^2.1.0",
@ -4238,7 +4259,6 @@
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz",
"integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==",
"dev": true,
"requires": { "requires": {
"lodash.sortby": "^4.7.0", "lodash.sortby": "^4.7.0",
"tr46": "^1.0.1", "tr46": "^1.0.1",
@ -4297,8 +4317,7 @@
"deep-is": { "deep-is": {
"version": "0.1.3", "version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ="
"dev": true
}, },
"default-compare": { "default-compare": {
"version": "1.0.0", "version": "1.0.0",
@ -4560,7 +4579,6 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz",
"integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==",
"dev": true,
"requires": { "requires": {
"webidl-conversions": "^4.0.2" "webidl-conversions": "^4.0.2"
} }
@ -4574,11 +4592,6 @@
"domelementtype": "1" "domelementtype": "1"
} }
}, },
"domino": {
"version": "2.1.6",
"resolved": "https://registry.npmmirror.com/domino/-/domino-2.1.6.tgz",
"integrity": "sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ=="
},
"domutils": { "domutils": {
"version": "1.6.2", "version": "1.6.2",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.6.2.tgz", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.6.2.tgz",
@ -4746,7 +4759,7 @@
}, },
"entity-decode": { "entity-decode": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmmirror.com/entity-decode/-/entity-decode-2.0.2.tgz", "resolved": "https://registry.npmjs.org/entity-decode/-/entity-decode-2.0.2.tgz",
"integrity": "sha512-5CCY/3ci4MC1m2jlumNjWd7VBFt4VfFnmSqSNmVcXq4gxM3Vmarxtt+SvmBnzwLS669MWdVuXboNVj1qN2esVg==", "integrity": "sha512-5CCY/3ci4MC1m2jlumNjWd7VBFt4VfFnmSqSNmVcXq4gxM3Vmarxtt+SvmBnzwLS669MWdVuXboNVj1qN2esVg==",
"requires": { "requires": {
"he": "^1.1.1" "he": "^1.1.1"
@ -4868,7 +4881,6 @@
"version": "1.11.0", "version": "1.11.0",
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.0.tgz", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.0.tgz",
"integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==", "integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==",
"dev": true,
"requires": { "requires": {
"esprima": "^3.1.3", "esprima": "^3.1.3",
"estraverse": "^4.2.0", "estraverse": "^4.2.0",
@ -4880,14 +4892,12 @@
"esprima": { "esprima": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
"integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM="
"dev": true
}, },
"source-map": { "source-map": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"optional": true "optional": true
} }
} }
@ -5339,14 +5349,12 @@
"estraverse": { "estraverse": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
"integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM="
"dev": true
}, },
"esutils": { "esutils": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
"dev": true
}, },
"etag": { "etag": {
"version": "1.8.1", "version": "1.8.1",
@ -5362,8 +5370,7 @@
"events": { "events": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
"dev": true
}, },
"eventsource-polyfill": { "eventsource-polyfill": {
"version": "0.9.6", "version": "0.9.6",
@ -6053,8 +6060,7 @@
"fast-levenshtein": { "fast-levenshtein": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
"dev": true
}, },
"fastparse": { "fastparse": {
"version": "1.1.1", "version": "1.1.1",
@ -8636,6 +8642,16 @@
"delegate": "^3.1.2" "delegate": "^3.1.2"
} }
}, },
"google-id-token-verifier": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/google-id-token-verifier/-/google-id-token-verifier-0.2.3.tgz",
"integrity": "sha1-nmt41FieLQUNqBYT+4kK26MKTqg=",
"requires": {
"request": "^2.65.0",
"rsa-pem-from-mod-exp": "^0.8.4",
"underscore": "^1.8.3"
}
},
"graceful-fs": { "graceful-fs": {
"version": "4.1.11", "version": "4.1.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
@ -8644,7 +8660,7 @@
}, },
"graphlib": { "graphlib": {
"version": "2.1.8", "version": "2.1.8",
"resolved": "https://registry.npmmirror.com/graphlib/-/graphlib-2.1.8.tgz", "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz",
"integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==",
"requires": { "requires": {
"lodash": "^4.17.15" "lodash": "^4.17.15"
@ -8652,7 +8668,7 @@
"dependencies": { "dependencies": {
"lodash": { "lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
} }
} }
@ -9053,7 +9069,6 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz",
"integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==",
"dev": true,
"requires": { "requires": {
"whatwg-encoding": "^1.0.1" "whatwg-encoding": "^1.0.1"
} }
@ -9624,8 +9639,7 @@
"ieee754": { "ieee754": {
"version": "1.1.8", "version": "1.1.8",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz",
"integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q="
"dev": true
}, },
"iferr": { "iferr": {
"version": "0.1.5", "version": "0.1.5",
@ -11224,6 +11238,11 @@
"url-regex": "^3.0.0" "url-regex": "^3.0.0"
} }
}, },
"jmespath": {
"version": "0.15.0",
"resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz",
"integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc="
},
"jpeg-js": { "jpeg-js": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.2.0.tgz", "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.2.0.tgz",
@ -11279,7 +11298,6 @@
"version": "11.12.0", "version": "11.12.0",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz",
"integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==",
"dev": true,
"requires": { "requires": {
"abab": "^2.0.0", "abab": "^2.0.0",
"acorn": "^5.5.3", "acorn": "^5.5.3",
@ -11312,20 +11330,17 @@
"acorn": { "acorn": {
"version": "5.7.1", "version": "5.7.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz",
"integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==", "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ=="
"dev": true
}, },
"sax": { "sax": {
"version": "1.2.4", "version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
"dev": true
}, },
"whatwg-url": { "whatwg-url": {
"version": "6.5.0", "version": "6.5.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz",
"integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==",
"dev": true,
"requires": { "requires": {
"lodash.sortby": "^4.7.0", "lodash.sortby": "^4.7.0",
"tr46": "^1.0.1", "tr46": "^1.0.1",
@ -11336,7 +11351,6 @@
"version": "5.2.2", "version": "5.2.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz",
"integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==",
"dev": true,
"requires": { "requires": {
"async-limiter": "~1.0.0" "async-limiter": "~1.0.0"
} }
@ -11535,8 +11549,7 @@
"left-pad": { "left-pad": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz",
"integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA=="
"dev": true
}, },
"leven": { "leven": {
"version": "2.1.0", "version": "2.1.0",
@ -11548,7 +11561,6 @@
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
"integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
"dev": true,
"requires": { "requires": {
"prelude-ls": "~1.1.2", "prelude-ls": "~1.1.2",
"type-check": "~0.3.2" "type-check": "~0.3.2"
@ -12501,7 +12513,7 @@
}, },
"minify": { "minify": {
"version": "4.1.3", "version": "4.1.3",
"resolved": "https://registry.npmmirror.com/minify/-/minify-4.1.3.tgz", "resolved": "https://registry.npmjs.org/minify/-/minify-4.1.3.tgz",
"integrity": "sha512-ykuscavxivSmVpcCzsXmsVTukWYLUUtPhHj0w2ILvHDGqC+hsuTCihBn9+PJBd58JNvWTNg9132J9nrrI2anzA==", "integrity": "sha512-ykuscavxivSmVpcCzsXmsVTukWYLUUtPhHj0w2ILvHDGqC+hsuTCihBn9+PJBd58JNvWTNg9132J9nrrI2anzA==",
"requires": { "requires": {
"clean-css": "^4.1.6", "clean-css": "^4.1.6",
@ -12515,7 +12527,7 @@
"dependencies": { "dependencies": {
"commander": { "commander": {
"version": "2.20.3", "version": "2.20.3",
"resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
}, },
"debug": { "debug": {
@ -12528,12 +12540,12 @@
}, },
"he": { "he": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmmirror.com/he/-/he-1.2.0.tgz", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
}, },
"html-minifier": { "html-minifier": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmmirror.com/html-minifier/-/html-minifier-4.0.0.tgz", "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz",
"integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==", "integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==",
"requires": { "requires": {
"camel-case": "^3.0.0", "camel-case": "^3.0.0",
@ -12547,7 +12559,7 @@
}, },
"ms": { "ms": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}, },
"uglify-js": { "uglify-js": {
@ -13330,8 +13342,7 @@
"nwsapi": { "nwsapi": {
"version": "2.0.8", "version": "2.0.8",
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.0.8.tgz", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.0.8.tgz",
"integrity": "sha512-7RZ+qbFGiVc6v14Y8DSZjPN1wZPOaMbiiP4tzf5eNuyOITAeOIA3cMhjuKUypVIqBgCSg1KaSyAv8Ocq/0ZJ1A==", "integrity": "sha512-7RZ+qbFGiVc6v14Y8DSZjPN1wZPOaMbiiP4tzf5eNuyOITAeOIA3cMhjuKUypVIqBgCSg1KaSyAv8Ocq/0ZJ1A=="
"dev": true
}, },
"oauth-sign": { "oauth-sign": {
"version": "0.9.0", "version": "0.9.0",
@ -13666,7 +13677,6 @@
"version": "0.8.2", "version": "0.8.2",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
"integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
"dev": true,
"requires": { "requires": {
"deep-is": "~0.1.3", "deep-is": "~0.1.3",
"fast-levenshtein": "~2.0.4", "fast-levenshtein": "~2.0.4",
@ -13679,8 +13689,7 @@
"wordwrap": { "wordwrap": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
"dev": true
} }
} }
}, },
@ -13998,8 +14007,7 @@
"parse5": { "parse5": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
"integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA=="
"dev": true
}, },
"parseurl": { "parseurl": {
"version": "1.3.2", "version": "1.3.2",
@ -14200,8 +14208,7 @@
"pn": { "pn": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
"integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA=="
"dev": true
}, },
"pngjs": { "pngjs": {
"version": "3.3.3", "version": "3.3.3",
@ -15104,8 +15111,7 @@
"prelude-ls": { "prelude-ls": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
"dev": true
}, },
"prepend-http": { "prepend-http": {
"version": "1.0.4", "version": "1.0.4",
@ -15269,8 +15275,7 @@
"punycode": { "punycode": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
"dev": true
}, },
"q": { "q": {
"version": "1.5.1", "version": "1.5.1",
@ -15296,8 +15301,7 @@
"querystring": { "querystring": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
"dev": true
}, },
"querystring-es3": { "querystring-es3": {
"version": "0.2.1", "version": "0.2.1",
@ -16246,7 +16250,6 @@
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz",
"integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=",
"dev": true,
"requires": { "requires": {
"lodash": "^4.13.1" "lodash": "^4.13.1"
} }
@ -16255,7 +16258,6 @@
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz", "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz",
"integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=", "integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=",
"dev": true,
"requires": { "requires": {
"request-promise-core": "1.1.1", "request-promise-core": "1.1.1",
"stealthy-require": "^1.1.0", "stealthy-require": "^1.1.0",
@ -16417,6 +16419,11 @@
"inherits": "^2.0.1" "inherits": "^2.0.1"
} }
}, },
"rsa-pem-from-mod-exp": {
"version": "0.8.4",
"resolved": "https://registry.npmjs.org/rsa-pem-from-mod-exp/-/rsa-pem-from-mod-exp-0.8.4.tgz",
"integrity": "sha1-NipCxtMEBW1JOz8SvOq7LGV2ptQ="
},
"rsvp": { "rsvp": {
"version": "3.6.2", "version": "3.6.2",
"resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz", "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz",
@ -17707,8 +17714,7 @@
"sax": { "sax": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
"integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=", "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o="
"dev": true
}, },
"schema-utils": { "schema-utils": {
"version": "0.3.0", "version": "0.3.0",
@ -18442,8 +18448,7 @@
"stealthy-require": { "stealthy-require": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
"dev": true
}, },
"stream-browserify": { "stream-browserify": {
"version": "2.0.1", "version": "2.0.1",
@ -19602,8 +19607,7 @@
"symbol-tree": { "symbol-tree": {
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz",
"integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY="
"dev": true
}, },
"table": { "table": {
"version": "4.0.2", "version": "4.0.2",
@ -20413,12 +20417,12 @@
}, },
"try-catch": { "try-catch": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmmirror.com/try-catch/-/try-catch-2.0.1.tgz", "resolved": "https://registry.npmjs.org/try-catch/-/try-catch-2.0.1.tgz",
"integrity": "sha512-LsOrmObN/2WdM+y2xG+t16vhYrQsnV8wftXIcIOWZhQcBJvKGYuamJGwnU98A7Jxs2oZNkJztXlphEOoA0DWqg==" "integrity": "sha512-LsOrmObN/2WdM+y2xG+t16vhYrQsnV8wftXIcIOWZhQcBJvKGYuamJGwnU98A7Jxs2oZNkJztXlphEOoA0DWqg=="
}, },
"try-to-catch": { "try-to-catch": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmmirror.com/try-to-catch/-/try-to-catch-1.1.1.tgz", "resolved": "https://registry.npmjs.org/try-to-catch/-/try-to-catch-1.1.1.tgz",
"integrity": "sha512-ikUlS+/BcImLhNYyIgZcEmq4byc31QpC+46/6Jm5ECWkVFhf8SM2Fp/0pMVXPX6vk45SMCwrP4Taxucne8I0VA==" "integrity": "sha512-ikUlS+/BcImLhNYyIgZcEmq4byc31QpC+46/6Jm5ECWkVFhf8SM2Fp/0pMVXPX6vk45SMCwrP4Taxucne8I0VA=="
}, },
"tryer": { "tryer": {
@ -20462,11 +20466,11 @@
} }
}, },
"turndown": { "turndown": {
"version": "7.1.1", "version": "4.0.2",
"resolved": "https://registry.npmmirror.com/turndown/-/turndown-7.1.1.tgz", "resolved": "https://registry.npmjs.org/turndown/-/turndown-4.0.2.tgz",
"integrity": "sha512-BEkXaWH7Wh7e9bd2QumhfAXk5g34+6QUmmWx+0q6ThaVOLuLUqsnkq35HQ5SBHSaxjSfSM7US5o4lhJNH7B9MA==", "integrity": "sha512-pqZ6WrHFGnxXC9q2xJ3Qa7EoLAwrojgFRajWZjxTKwbz9vnNnyi8lLjiD5h86UTPOcMlEyHjm6NMhjEDdlc25A==",
"requires": { "requires": {
"domino": "^2.1.6" "jsdom": "^11.9.0"
} }
}, },
"tweetnacl": { "tweetnacl": {
@ -20485,7 +20489,6 @@
"version": "0.3.2", "version": "0.3.2",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
"integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
"dev": true,
"requires": { "requires": {
"prelude-ls": "~1.1.2" "prelude-ls": "~1.1.2"
} }
@ -20565,8 +20568,7 @@
"underscore": { "underscore": {
"version": "1.8.3", "version": "1.8.3",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
"integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=", "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI="
"dev": true
}, },
"undertaker": { "undertaker": {
"version": "1.3.0", "version": "1.3.0",
@ -20841,6 +20843,15 @@
"integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
"dev": true "dev": true
}, },
"url": {
"version": "0.10.3",
"resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
"integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
"requires": {
"punycode": "1.3.2",
"querystring": "0.2.0"
}
},
"url-loader": { "url-loader": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.1.tgz", "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.1.tgz",
@ -20965,6 +20976,11 @@
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
"dev": true "dev": true
}, },
"uuid": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz",
"integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g=="
},
"v8flags": { "v8flags": {
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz",
@ -21320,7 +21336,6 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz",
"integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=",
"dev": true,
"requires": { "requires": {
"browser-process-hrtime": "^0.1.2" "browser-process-hrtime": "^0.1.2"
} }
@ -21742,7 +21757,6 @@
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.4.tgz", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.4.tgz",
"integrity": "sha512-vM9KWN6MP2mIHZ86ytcyIv7e8Cj3KTfO2nd2c8PFDqcI4bxFmQp83ibq4wadq7rL9l9sZV6o9B0LTt8ygGAAXg==", "integrity": "sha512-vM9KWN6MP2mIHZ86ytcyIv7e8Cj3KTfO2nd2c8PFDqcI4bxFmQp83ibq4wadq7rL9l9sZV6o9B0LTt8ygGAAXg==",
"dev": true,
"requires": { "requires": {
"iconv-lite": "0.4.23" "iconv-lite": "0.4.23"
}, },
@ -21751,7 +21765,6 @@
"version": "0.4.23", "version": "0.4.23",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
"integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
"dev": true,
"requires": { "requires": {
"safer-buffer": ">= 2.1.2 < 3" "safer-buffer": ">= 2.1.2 < 3"
} }
@ -21761,8 +21774,7 @@
"whatwg-mimetype": { "whatwg-mimetype": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz",
"integrity": "sha512-FKxhYLytBQiUKjkYteN71fAUA3g6KpNXoho1isLiLSB3N1G4F35Q5vUxWfKFhBwi5IWF27VE6WxhrnnC+m0Mew==", "integrity": "sha512-FKxhYLytBQiUKjkYteN71fAUA3g6KpNXoho1isLiLSB3N1G4F35Q5vUxWfKFhBwi5IWF27VE6WxhrnnC+m0Mew=="
"dev": true
}, },
"whatwg-url": { "whatwg-url": {
"version": "6.4.0", "version": "6.4.0",
@ -21941,8 +21953,7 @@
"xml-name-validator": { "xml-name-validator": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
"integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw=="
"dev": true
}, },
"xml-parse-from-string": { "xml-parse-from-string": {
"version": "1.0.1", "version": "1.0.1",

View File

@ -1,6 +1,6 @@
{ {
"name": "stackedit", "name": "stackedit",
"version": "5.15.21", "version": "5.15.15",
"description": "免费, 开源, 功能齐全的 Markdown 编辑器", "description": "免费, 开源, 功能齐全的 Markdown 编辑器",
"author": "Benoit Schweblin, 豆萁", "author": "Benoit Schweblin, 豆萁",
"license": "Apache-2.0", "license": "Apache-2.0",
@ -27,6 +27,7 @@
"dependencies": { "dependencies": {
"@vue/test-utils": "^1.0.0-beta.16", "@vue/test-utils": "^1.0.0-beta.16",
"abcjs": "^5.2.0", "abcjs": "^5.2.0",
"aws-sdk": "^2.317.0",
"babel-runtime": "^6.26.0", "babel-runtime": "^6.26.0",
"bezier-easing": "^1.1.0", "bezier-easing": "^1.1.0",
"body-parser": "^1.18.2", "body-parser": "^1.18.2",
@ -34,6 +35,7 @@
"compression": "^1.7.0", "compression": "^1.7.0",
"diff-match-patch": "^1.0.0", "diff-match-patch": "^1.0.0",
"file-saver": "^1.3.8", "file-saver": "^1.3.8",
"google-id-token-verifier": "^0.2.3",
"handlebars": "^4.0.10", "handlebars": "^4.0.10",
"indexeddbshim": "^3.6.2", "indexeddbshim": "^3.6.2",
"js-yaml": "^3.11.0", "js-yaml": "^3.11.0",
@ -55,7 +57,7 @@
"request": "^2.85.0", "request": "^2.85.0",
"serve-static": "^1.13.2", "serve-static": "^1.13.2",
"tmp": "^0.0.33", "tmp": "^0.0.33",
"turndown": "^7.1.1", "turndown": "^4.0.2",
"vue": "^2.5.16", "vue": "^2.5.16",
"vuex": "^3.0.1" "vuex": "^3.0.1"
}, },

View File

@ -1,5 +1,7 @@
const pandocPath = process.env.PANDOC_PATH || 'pandoc'; const pandocPath = process.env.PANDOC_PATH || 'pandoc';
const wkhtmltopdfPath = process.env.WKHTMLTOPDF_PATH || 'wkhtmltopdf'; const wkhtmltopdfPath = process.env.WKHTMLTOPDF_PATH || 'wkhtmltopdf';
const userBucketName = process.env.USER_BUCKET_NAME || 'stackedit-users';
const paypalUri = process.env.PAYPAL_URI || 'https://www.paypal.com/cgi-bin/webscr';
const paypalReceiverEmail = process.env.PAYPAL_RECEIVER_EMAIL; const paypalReceiverEmail = process.env.PAYPAL_RECEIVER_EMAIL;
const dropboxAppKey = process.env.DROPBOX_APP_KEY; const dropboxAppKey = process.env.DROPBOX_APP_KEY;
@ -14,13 +16,12 @@ const wordpressClientId = process.env.WORDPRESS_CLIENT_ID;
const giteaClientId = process.env.GITEA_CLIENT_ID; const giteaClientId = process.env.GITEA_CLIENT_ID;
const giteaClientSecret = process.env.GITEA_CLIENT_SECRET; const giteaClientSecret = process.env.GITEA_CLIENT_SECRET;
const giteaUrl = process.env.GITEA_URL; const giteaUrl = process.env.GITEA_URL;
const gitlabClientId = process.env.GITLAB_CLIENT_ID;
const gitlabClientSecret = process.env.GITLAB_CLIENT_SECRET;
const gitlabUrl = process.env.GITLAB_URL;
exports.values = { exports.values = {
pandocPath, pandocPath,
wkhtmltopdfPath, wkhtmltopdfPath,
userBucketName,
paypalUri,
paypalReceiverEmail, paypalReceiverEmail,
dropboxAppKey, dropboxAppKey,
dropboxAppKeyFull, dropboxAppKeyFull,
@ -34,9 +35,6 @@ exports.values = {
giteaClientId, giteaClientId,
giteaClientSecret, giteaClientSecret,
giteaUrl, giteaUrl,
gitlabClientId,
gitlabClientSecret,
gitlabUrl,
}; };
exports.publicValues = { exports.publicValues = {
@ -49,6 +47,4 @@ exports.publicValues = {
allowSponsorship: !!paypalReceiverEmail, allowSponsorship: !!paypalReceiverEmail,
giteaClientId, giteaClientId,
giteaUrl, giteaUrl,
gitlabClientId,
gitlabUrl,
}; };

View File

@ -1,3 +1,4 @@
const qs = require('qs');
const request = require('request'); const request = require('request');
const conf = require('./conf'); const conf = require('./conf');

View File

@ -1,40 +0,0 @@
const request = require('request');
const conf = require('./conf');
function gitlabToken(queryParam) {
return new Promise((resolve, reject) => {
request({
method: 'POST',
url: `${conf.values.gitlabUrl}/oauth/token`,
headers: {
'content-type': 'application/json',
},
json: true,
qs: {
...queryParam,
client_id: conf.values.gitlabClientId,
client_secret: conf.values.gitlabClientSecret,
},
}, (err, res, body) => {
if (err) {
reject(err);
}
const token = body.access_token;
if (token) {
resolve(body);
} else {
reject(res.statusCode + ',body:' + JSON.stringify(body));
}
});
});
}
exports.gitlabToken = (req, res) => {
gitlabToken(req.query)
.then(
tokenBody => res.send(tokenBody),
err => res
.status(400)
.send(err ? err.message || err.toString() : 'bad_code'),
);
};

View File

@ -2,10 +2,10 @@ const compression = require('compression');
const serveStatic = require('serve-static'); const serveStatic = require('serve-static');
const bodyParser = require('body-parser'); const bodyParser = require('body-parser');
const path = require('path'); const path = require('path');
const user = require('./user');
const github = require('./github'); const github = require('./github');
const gitee = require('./gitee'); const gitee = require('./gitee');
const gitea = require('./gitea'); const gitea = require('./gitea');
const gitlab = require('./gitlab');
const pdf = require('./pdf'); const pdf = require('./pdf');
const pandoc = require('./pandoc'); const pandoc = require('./pandoc');
const conf = require('./conf'); const conf = require('./conf');
@ -29,10 +29,13 @@ module.exports = (app) => {
app.get('/oauth2/githubToken', github.githubToken); app.get('/oauth2/githubToken', github.githubToken);
app.get('/oauth2/giteeToken', gitee.giteeToken); app.get('/oauth2/giteeToken', gitee.giteeToken);
app.get('/oauth2/giteaToken', gitea.giteaToken); app.get('/oauth2/giteaToken', gitea.giteaToken);
app.get('/oauth2/gitlabToken', gitlab.gitlabToken);
app.get('/conf', (req, res) => res.send(conf.publicValues)); app.get('/conf', (req, res) => res.send(conf.publicValues));
app.get('/userInfo', user.userInfo);
app.post('/pdfExport', pdf.generate); app.post('/pdfExport', pdf.generate);
app.post('/pandocExport', pandoc.generate); app.post('/pandocExport', pandoc.generate);
app.post('/paypalIpn', bodyParser.urlencoded({
extended: false,
}), user.paypalIpn);
app.get('/giteeClientId', (req, res) => { app.get('/giteeClientId', (req, res) => {
const giteeClientIds = conf.values.giteeClientId.split(','); const giteeClientIds = conf.values.giteeClientId.split(',');
// 仅一个 则直接返回 // 仅一个 则直接返回
@ -61,24 +64,21 @@ module.exports = (app) => {
// Google Drive action receiver // Google Drive action receiver
app.get('/googleDriveAction', (req, res) => app.get('/googleDriveAction', (req, res) =>
res.redirect(`./app#providerId=googleDrive&state=${encodeURIComponent(req.query.state)}`)); res.redirect(`./app#providerId=googleDrive&state=${encodeURIComponent(req.query.state)}`));
// Serve the static folder with 30 day max-age // Serve the static folder with 1 day max-age
app.use('/themes', serveStatic(resolvePath('static/themes'), { app.use('/themes', serveStatic(resolvePath('static/themes'), {
maxAge: '5d',
}));
// Serve style.css with 1 day max-age
app.get('/style.css', (req, res) => res.sendFile(resolvePath('dist/style.css'), {
maxAge: '1d', maxAge: '1d',
})); }));
// Serve share.html
app.get('/share.html', (req, res) => res.sendFile(resolvePath('static/landing/share.html')));
app.get('/gistshare.html', (req, res) => res.sendFile(resolvePath('static/landing/gistshare.html')));
// Serve static resources // Serve static resources
if (process.env.NODE_ENV === 'production') { if (process.env.NODE_ENV === 'production') {
// Serve index.html in /app // Serve index.html in /app
app.get('/app', (req, res) => res.sendFile(resolvePath('dist/index.html'))); app.get('/app', (req, res) => res.sendFile(resolvePath('dist/index.html')));
// Serve style.css with 1 day max-age
app.get('/style.css', (req, res) => res.sendFile(resolvePath('dist/style.css'), {
maxAge: '1d',
}));
// Serve the static folder with 1 year max-age // Serve the static folder with 1 year max-age
app.use('/static', serveStatic(resolvePath('dist/static'), { app.use('/static', serveStatic(resolvePath('dist/static'), {
maxAge: '1y', maxAge: '1y',

116
server/user.js Normal file
View File

@ -0,0 +1,116 @@
const request = require('request');
const AWS = require('aws-sdk');
const verifier = require('google-id-token-verifier');
const conf = require('./conf');
const s3Client = new AWS.S3();
const cb = (resolve, reject) => (err, res) => {
if (err) {
reject(err);
} else {
resolve(res);
}
};
exports.getUser = id => new Promise((resolve, reject) => {
s3Client.getObject({
Bucket: conf.values.userBucketName,
Key: id,
}, cb(resolve, reject));
})
.then(
res => JSON.parse(`${res.Body}`),
(err) => {
if (err.code !== 'NoSuchKey') {
throw err;
}
},
);
exports.putUser = (id, user) => new Promise((resolve, reject) => {
s3Client.putObject({
Bucket: conf.values.userBucketName,
Key: id,
Body: JSON.stringify(user),
}, cb(resolve, reject));
});
exports.removeUser = id => new Promise((resolve, reject) => {
s3Client.deleteObject({
Bucket: conf.values.userBucketName,
Key: id,
}, cb(resolve, reject));
});
exports.getUserFromToken = idToken => new Promise((resolve, reject) => verifier
.verify(idToken, conf.values.googleClientId, cb(resolve, reject)))
.then(tokenInfo => exports.getUser(tokenInfo.sub));
exports.userInfo = (req, res) => exports.getUserFromToken(req.query.idToken)
.then(
user => res.send(Object.assign({
sponsorUntil: 0,
}, user)),
err => res
.status(400)
.send(err ? err.message || err.toString() : 'invalid_token'),
);
exports.paypalIpn = (req, res, next) => Promise.resolve()
.then(() => {
const userId = req.body.custom;
const paypalEmail = req.body.payer_email;
const gross = parseFloat(req.body.mc_gross);
let sponsorUntil;
if (gross === 5) {
sponsorUntil = Date.now() + (3 * 31 * 24 * 60 * 60 * 1000); // 3 months
} else if (gross === 15) {
sponsorUntil = Date.now() + (366 * 24 * 60 * 60 * 1000); // 1 year
} else if (gross === 25) {
sponsorUntil = Date.now() + (2 * 366 * 24 * 60 * 60 * 1000); // 2 years
} else if (gross === 50) {
sponsorUntil = Date.now() + (5 * 366 * 24 * 60 * 60 * 1000); // 5 years
}
if (
req.body.receiver_email !== conf.values.paypalReceiverEmail ||
req.body.payment_status !== 'Completed' ||
req.body.mc_currency !== 'USD' ||
(req.body.txn_type !== 'web_accept' && req.body.txn_type !== 'subscr_payment') ||
!userId || !sponsorUntil
) {
// Ignoring PayPal IPN
return res.end();
}
// Processing PayPal IPN
req.body.cmd = '_notify-validate';
return new Promise((resolve, reject) => request.post({
uri: conf.values.paypalUri,
form: req.body,
}, (err, response, body) => {
if (err) {
reject(err);
} else if (body !== 'VERIFIED') {
reject(new Error('PayPal IPN unverified'));
} else {
resolve();
}
}))
.then(() => exports.putUser(userId, {
paypalEmail,
sponsorUntil,
}))
.then(() => res.end());
})
.catch(next);
exports.checkSponsor = (idToken) => {
if (!conf.publicValues.allowSponsorship) {
return Promise.resolve(true);
}
if (!idToken) {
return Promise.resolve(false);
}
return exports.getUserFromToken(idToken)
.then(userInfo => userInfo && userInfo.sponsorUntil > Date.now(), () => false);
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -22,8 +22,6 @@ import networkSvc from '../services/networkSvc';
import tempFileSvc from '../services/tempFileSvc'; import tempFileSvc from '../services/tempFileSvc';
import store from '../store'; import store from '../store';
import './common/vueGlobals'; import './common/vueGlobals';
import utils from '../services/utils';
import providerRegistry from '../services/providers/common/providerRegistry';
const themeClasses = { const themeClasses = {
light: ['app--light'], light: ['app--light'],
@ -51,49 +49,14 @@ export default {
close() { close() {
tempFileSvc.close(); tempFileSvc.close();
}, },
//
viewFileByPath(path) {
// md
if (!path) {
return;
}
const currDirNode = store.getters['explorer/selectedNodeFolder'];
if (path.slice(-3) === '.md') {
const rootNode = store.getters['explorer/rootNode'];
const node = utils.findNodeByPath(rootNode, currDirNode, path);
if (!node) {
return;
}
store.commit('explorer/setSelectedId', node.item.id);
// Prevent from freezing the UI while loading the file
setTimeout(() => {
store.commit('file/setCurrentId', node.item.id);
}, 10);
} else {
const workspace = store.getters['workspace/currentWorkspace'];
const provider = providerRegistry.providersById[workspace.providerId];
if (provider == null) {
return;
}
const absolutePath = utils.getAbsoluteFilePath(currDirNode, path);
const url = provider.getFilePathUrl(absolutePath);
if (url) {
window.open(url, '_blank');
}
}
},
}, },
async created() { async created() {
window.viewFileByPath = this.viewFileByPath;
try { try {
await syncSvc.init(); await syncSvc.init();
await networkSvc.init(); await networkSvc.init();
// store // store
const editTheme = localStorage.getItem('theme/currEditTheme'); const editTheme = localStorage.getItem('theme/currEditTheme');
store.dispatch('theme/setEditTheme', editTheme || 'default'); store.dispatch('theme/setEditTheme', editTheme || 'default');
// store
const previewTheme = localStorage.getItem('theme/currPreviewTheme');
store.dispatch('theme/setPreviewTheme', previewTheme || 'default');
this.ready = true; this.ready = true;
tempFileSvc.setReady(); tempFileSvc.setReady();
} catch (err) { } catch (err) {

View File

@ -12,8 +12,8 @@
<icon-select-theme></icon-select-theme> <icon-select-theme></icon-select-theme>
</dropdown-menu> </dropdown-menu>
</li> </li>
<li class="after"> <li title="Markdown语法帮助">
<icon-ellipsis></icon-ellipsis> <a @click="showHelp"><icon-help-circle></icon-help-circle></a>
</li> </li>
</ul> </ul>
</div> </div>
@ -123,6 +123,10 @@ export default {
store.dispatch('data/setSideBarPanel', 'editTheme'); store.dispatch('data/setSideBarPanel', 'editTheme');
} }
}, },
showHelp() {
this.toggleSideBar(true);
store.dispatch('data/setSideBarPanel', 'help');
},
}, },
}; };
</script> </script>
@ -133,44 +137,20 @@ export default {
.editor-in-page-buttons { .editor-in-page-buttons {
position: absolute; position: absolute;
top: 0; top: 0;
left: -108px; right: 10px;
height: 34px; height: 34px;
padding: 5px; padding: 5px;
background-color: rgba(84, 96, 114, 0.4); background-color: rgba(187, 187, 187, 0.05);
border-radius: $border-radius-base; border-radius: $border-radius-base;
transition: 0.5s;
display: flex;
.dropdown-menu {
display: none;
.dropdown-menu-items {
right: unset;
left: 0;
}
}
&:active,
&:focus,
&:hover {
left: 0;
transition: 0.5s;
background-color: #546072;
.dropdown-menu {
display: block;
}
}
ul { ul {
padding: 0; padding: 0;
margin-left: 10px; margin-left: 10px;
line-height: 20px;
li { li {
line-height: 16px;
width: 16px; width: 16px;
display: inline-block; display: inline-block;
vertical-align: middle;
list-style: none; list-style: none;
cursor: pointer; cursor: pointer;
font-size: 14px; font-size: 14px;
@ -180,7 +160,7 @@ export default {
.icon { .icon {
color: #dea731; color: #dea731;
opacity: 0.7; opacity: 0.3;
&:active, &:active,
&:focus, &:focus,
@ -188,10 +168,5 @@ export default {
opacity: 1; opacity: 1;
} }
} }
.after {
margin-left: 0;
margin-right: -6px;
}
} }
</style> </style>

View File

@ -14,7 +14,6 @@
</div> </div>
<explorer-node v-for="node in node.files" :key="node.item.id" :node="node" :depth="depth + 1"></explorer-node> <explorer-node v-for="node in node.files" :key="node.item.id" :node="node" :depth="depth + 1"></explorer-node>
</div> </div>
<button ref="copyId" v-clipboard="copyPath()" @click="info('路径已复制到剪切板!')" style="display: none;"></button>
</div> </div>
</template> </template>
@ -24,7 +23,6 @@ import workspaceSvc from '../services/workspaceSvc';
import explorerSvc from '../services/explorerSvc'; import explorerSvc from '../services/explorerSvc';
import store from '../store'; import store from '../store';
import badgeSvc from '../services/badgeSvc'; import badgeSvc from '../services/badgeSvc';
import utils from '../services/utils';
export default { export default {
name: 'explorer-node', // Required for recursivity name: 'explorer-node', // Required for recursivity
@ -82,9 +80,6 @@ export default {
...mapActions('explorer', [ ...mapActions('explorer', [
'setDragTarget', 'setDragTarget',
]), ]),
...mapActions('notification', [
'info',
]),
select(id = this.node.item.id, doOpen = true) { select(id = this.node.item.id, doOpen = true) {
const node = store.getters['explorer/nodeMap'][id]; const node = store.getters['explorer/nodeMap'][id];
if (!node) { if (!node) {
@ -149,11 +144,6 @@ export default {
// See https://stackoverflow.com/a/3977637/1333165 // See https://stackoverflow.com/a/3977637/1333165
evt.dataTransfer.setData('Text', ''); evt.dataTransfer.setData('Text', '');
}, },
copyPath() {
let path = utils.getAbsoluteDir(this.node).replaceAll(' ', '%20');
path = path.indexOf('/') === 0 ? path : `/${path}`;
return this.node.isFolder ? path : `${path}.md`;
},
onDrop() { onDrop() {
const sourceNode = store.getters['explorer/dragSourceNode']; const sourceNode = store.getters['explorer/dragSourceNode'];
const targetNode = store.getters['explorer/dragTargetNodeFolder']; const targetNode = store.getters['explorer/dragTargetNodeFolder'];
@ -179,26 +169,22 @@ export default {
top: evt.clientY, top: evt.clientY,
}, },
items: [{ items: [{
name: '新建文件', name: 'New file',
disabled: !this.node.isFolder || this.node.isTrash, disabled: !this.node.isFolder || this.node.isTrash,
perform: () => explorerSvc.newItem(false), perform: () => explorerSvc.newItem(false),
}, { }, {
name: '新建文件夹', name: 'New folder',
disabled: !this.node.isFolder || this.node.isTrash || this.node.isTemp, disabled: !this.node.isFolder || this.node.isTrash || this.node.isTemp,
perform: () => explorerSvc.newItem(true), perform: () => explorerSvc.newItem(true),
}, { }, {
type: 'separator', type: 'separator',
}, { }, {
name: '重命名', name: 'Rename',
disabled: this.node.isTrash || this.node.isTemp, disabled: this.node.isTrash || this.node.isTemp,
perform: () => this.setEditingId(this.node.item.id), perform: () => this.setEditingId(this.node.item.id),
}, { }, {
name: '删除', name: 'Delete',
perform: () => explorerSvc.deleteItem(), perform: () => explorerSvc.deleteItem(),
}, {
name: '复制路径',
disabled: this.node.isTrash || this.node.isTemp,
perform: () => this.$refs.copyId.click(),
}], }],
}); });
if (item) { if (item) {

View File

@ -9,7 +9,7 @@
<navigation-bar></navigation-bar> <navigation-bar></navigation-bar>
</div> </div>
<div class="layout__panel flex flex--row" :style="{height: styles.innerHeight + 'px'}"> <div class="layout__panel flex flex--row" :style="{height: styles.innerHeight + 'px'}">
<div class="layout__panel layout__panel--editor" :class="editTheme" v-show="styles.showEditor" :style="{width: (styles.editorWidth + styles.editorGutterWidth) + 'px', fontSize: styles.fontSize + 'px'}"> <div class="layout__panel layout__panel--editor" :class="currTheme" v-show="styles.showEditor" :style="{width: (styles.editorWidth + styles.editorGutterWidth) + 'px', fontSize: styles.fontSize + 'px'}">
<div class="gutter" :style="{left: styles.editorGutterLeft + 'px'}"> <div class="gutter" :style="{left: styles.editorGutterLeft + 'px'}">
<div class="gutter__background" v-if="styles.editorGutterWidth" :style="{width: styles.editorGutterWidth + 'px'}"></div> <div class="gutter__background" v-if="styles.editorGutterWidth" :style="{width: styles.editorGutterWidth + 'px'}"></div>
</div> </div>
@ -28,7 +28,6 @@
<div class="gutter__background" v-if="styles.previewGutterWidth" :style="{width: styles.previewGutterWidth + 'px'}"></div> <div class="gutter__background" v-if="styles.previewGutterWidth" :style="{width: styles.previewGutterWidth + 'px'}"></div>
</div> </div>
<preview></preview> <preview></preview>
<preview-in-page-buttons></preview-in-page-buttons>
<div class="gutter" :style="{left: styles.previewGutterLeft + 'px'}"> <div class="gutter" :style="{left: styles.previewGutterLeft + 'px'}">
<sticky-comment v-if="styles.previewGutterWidth && stickyComment === 'top'"></sticky-comment> <sticky-comment v-if="styles.previewGutterWidth && stickyComment === 'top'"></sticky-comment>
<current-discussion v-if="styles.previewGutterWidth"></current-discussion> <current-discussion v-if="styles.previewGutterWidth"></current-discussion>
@ -61,7 +60,6 @@ import Editor from './Editor';
import Preview from './Preview'; import Preview from './Preview';
import Tour from './Tour'; import Tour from './Tour';
import EditorInPageButtons from './EditorInPageButtons'; import EditorInPageButtons from './EditorInPageButtons';
import PreviewInPageButtons from './PreviewInPageButtons';
import StickyComment from './gutters/StickyComment'; import StickyComment from './gutters/StickyComment';
import CurrentDiscussion from './gutters/CurrentDiscussion'; import CurrentDiscussion from './gutters/CurrentDiscussion';
import FindReplace from './FindReplace'; import FindReplace from './FindReplace';
@ -80,7 +78,6 @@ export default {
Preview, Preview,
Tour, Tour,
EditorInPageButtons, EditorInPageButtons,
PreviewInPageButtons,
StickyComment, StickyComment,
CurrentDiscussion, CurrentDiscussion,
FindReplace, FindReplace,
@ -105,7 +102,7 @@ export default {
...mapGetters('theme', [ ...mapGetters('theme', [
'currEditTheme', 'currEditTheme',
]), ]),
editTheme() { currTheme() {
return `edit-theme--${this.currEditTheme || 'default'}`; return `edit-theme--${this.currEditTheme || 'default'}`;
}, },
showFindReplace() { showFindReplace() {

View File

@ -10,7 +10,6 @@
<div class="modal__button-bar"> <div class="modal__button-bar">
<button class="button" v-if="simpleModal.rejectText" @click="config.reject()">{{simpleModal.rejectText}}</button> <button class="button" v-if="simpleModal.rejectText" @click="config.reject()">{{simpleModal.rejectText}}</button>
<button class="button button--resolve" v-if="simpleModal.resolveText" @click="config.resolve()">{{simpleModal.resolveText}}</button> <button class="button button--resolve" v-if="simpleModal.resolveText" @click="config.resolve()">{{simpleModal.resolveText}}</button>
<button v-for="(item, idx) in (simpleModal.resolveArray || [])" class="button button--resolve" @click="config.resolve(item.value)">{{item.text}}</button>
</div> </div>
</modal-inner> </modal-inner>
</div> </div>
@ -42,7 +41,6 @@ import BadgeManagementModal from './modals/BadgeManagementModal';
import SponsorModal from './modals/SponsorModal'; import SponsorModal from './modals/SponsorModal';
import CommitMessageModal from './modals/CommitMessageModal'; import CommitMessageModal from './modals/CommitMessageModal';
import WorkspaceImgPathModal from './modals/WorkspaceImgPathModal'; import WorkspaceImgPathModal from './modals/WorkspaceImgPathModal';
import ChatGptModal from './modals/ChatGptModal';
// Providers // Providers
import GooglePhotoModal from './modals/providers/GooglePhotoModal'; import GooglePhotoModal from './modals/providers/GooglePhotoModal';
@ -66,8 +64,6 @@ import GiteeOpenModal from './modals/providers/GiteeOpenModal';
import GiteeSaveModal from './modals/providers/GiteeSaveModal'; import GiteeSaveModal from './modals/providers/GiteeSaveModal';
import GiteeWorkspaceModal from './modals/providers/GiteeWorkspaceModal'; import GiteeWorkspaceModal from './modals/providers/GiteeWorkspaceModal';
import GiteePublishModal from './modals/providers/GiteePublishModal'; import GiteePublishModal from './modals/providers/GiteePublishModal';
import GiteeGistSyncModal from './modals/providers/GiteeGistSyncModal';
import GiteeGistPublishModal from './modals/providers/GiteeGistPublishModal';
import GitlabAccountModal from './modals/providers/GitlabAccountModal'; import GitlabAccountModal from './modals/providers/GitlabAccountModal';
import GitlabOpenModal from './modals/providers/GitlabOpenModal'; import GitlabOpenModal from './modals/providers/GitlabOpenModal';
import GitlabPublishModal from './modals/providers/GitlabPublishModal'; import GitlabPublishModal from './modals/providers/GitlabPublishModal';
@ -113,7 +109,6 @@ export default {
SponsorModal, SponsorModal,
CommitMessageModal, CommitMessageModal,
WorkspaceImgPathModal, WorkspaceImgPathModal,
ChatGptModal,
// Providers // Providers
GooglePhotoModal, GooglePhotoModal,
GoogleDriveAccountModal, GoogleDriveAccountModal,
@ -136,8 +131,6 @@ export default {
GiteeSaveModal, GiteeSaveModal,
GiteeWorkspaceModal, GiteeWorkspaceModal,
GiteePublishModal, GiteePublishModal,
GiteeGistSyncModal,
GiteeGistPublishModal,
GitlabAccountModal, GitlabAccountModal,
GitlabOpenModal, GitlabOpenModal,
GitlabPublishModal, GitlabPublishModal,
@ -188,7 +181,6 @@ export default {
// User has to sign in // User has to sign in
await store.dispatch('modal/open', 'signInForSponsorship'); await store.dispatch('modal/open', 'signInForSponsorship');
await giteeHelper.signin(); await giteeHelper.signin();
await syncSvc.afterSignIn();
syncSvc.requestSync(); syncSvc.requestSync();
} }
if (!store.getters.isSponsor) { if (!store.getters.isSponsor) {

View File

@ -114,8 +114,7 @@ export default {
publishLocations: 'current', publishLocations: 'current',
}), }),
pagedownButtons() { pagedownButtons() {
const buttonShowObj = store.getters['data/computedSettings'].editor.headButtons; return pagedownButtons.map(button => ({
return pagedownButtons.filter(it => buttonShowObj[it.method]).map(button => ({
...button, ...button,
titleWithShortcut: `${button.title}${getShortcut(button.method)}`, titleWithShortcut: `${button.title}${getShortcut(button.method)}`,
iconClass: `icon-${button.icon}`, iconClass: `icon-${button.icon}`,

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="preview"> <div class="preview">
<div class="preview__inner-1" @click="onClick" @scroll="onScroll"> <div class="preview__inner-1" @click="onClick" @scroll="onScroll">
<div class="preview__inner-2" :class="previewTheme" :style="{padding: styles.previewPadding}"> <div class="preview__inner-2" :style="{padding: styles.previewPadding}">
</div> </div>
<div class="gutter" :style="{left: styles.previewGutterLeft + 'px'}"> <div class="gutter" :style="{left: styles.previewGutterLeft + 'px'}">
<comment-list v-if="styles.previewGutterWidth"></comment-list> <comment-list v-if="styles.previewGutterWidth"></comment-list>
@ -37,15 +37,9 @@ export default {
...mapGetters('file', [ ...mapGetters('file', [
'isCurrentTemp', 'isCurrentTemp',
]), ]),
...mapGetters('theme', [
'currPreviewTheme',
]),
...mapGetters('layout', [ ...mapGetters('layout', [
'styles', 'styles',
]), ]),
previewTheme() {
return `preview-theme--${this.currPreviewTheme || 'default'}`;
},
}, },
methods: { methods: {
...mapActions('data', [ ...mapActions('data', [

View File

@ -1,212 +0,0 @@
<template>
<div class="preview-in-page-buttons">
<ul>
<li class="before">
<icon-ellipsis></icon-ellipsis>
</li>
<li title="分享">
<a href="javascript:void(0)" @click="share"><icon-share></icon-share></a>
</li>
<li title="切换预览主题">
<dropdown-menu :selected="selectedTheme" :options="allThemes" :closeOnItemClick="false" @change="changeTheme">
<icon-select-theme></icon-select-theme>
</dropdown-menu>
</li>
<li title="Markdown语法帮助">
<a href="javascript:void(0)" @click="showHelp"><icon-help-circle></icon-help-circle></a>
</li>
</ul>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
// import juice from 'juice';
import store from '../store';
import DropdownMenu from './common/DropdownMenu';
import publishSvc from '../services/publishSvc';
import giteeGistProvider from '../services/providers/giteeGistProvider';
import gistProvider from '../services/providers/gistProvider';
export default {
components: {
DropdownMenu,
},
data: () => ({
allThemes: [{
name: '默认主题',
value: 'default',
}, {
name: '凝夜紫',
value: 'ningyezi',
}, {
name: '草原绿',
value: 'caoyuangreen',
}, {
name: '雁栖湖',
value: 'yanqihu',
}, {
name: '灵动蓝',
value: 'activeblue',
}, {
name: '极客黑',
value: 'jikebrack',
}, {
name: '极简黑',
value: 'simplebrack',
}, {
name: '全栈蓝',
value: 'allblue',
}, {
name: '自定义',
value: 'custom',
}],
baseCss: '',
sharing: false,
}),
computed: {
...mapGetters('theme', [
'currPreviewTheme',
'customPreviewThemeStyle',
]),
...mapGetters('publishLocation', {
publishLocations: 'current',
}),
selectedTheme() {
return {
value: this.currPreviewTheme || 'default',
};
},
},
methods: {
...mapActions('data', [
'toggleSideBar',
]),
async changeTheme(item) {
await store.dispatch('theme/setPreviewTheme', item.value);
//
if (item.value === 'custom' && !this.customPreviewThemeStyle) {
this.toggleSideBar(true);
store.dispatch('data/setSideBarPanel', 'previewTheme');
}
},
showHelp() {
this.toggleSideBar(true);
store.dispatch('data/setSideBarPanel', 'help');
},
async share() {
if (this.sharing) {
store.dispatch('notification/info', '分享链接创建中...请稍后再试');
return;
}
try {
const currentFile = store.getters['file/current'];
await store.dispatch('modal/open', { type: 'shareHtmlPre', name: currentFile.name });
this.sharing = true;
const mainToken = store.getters['workspace/mainWorkspaceToken'];
if (!mainToken) {
store.dispatch('notification/info', '登录主文档空间之后才可使用分享功能!');
return;
}
let tempGistId = null;
const isGithub = mainToken.providerId === 'githubAppData';
const gistProviderId = isGithub ? 'gist' : 'giteegist';
const filterLocations = this.publishLocations.filter(it => it.providerId === gistProviderId
&& it.url && it.gistId);
if (filterLocations.length > 0) {
tempGistId = filterLocations[0].gistId;
}
const location = (isGithub ? gistProvider : giteeGistProvider).makeLocation(
mainToken,
`分享-${currentFile.name}`,
true,
null,
);
location.templateId = 'styledHtmlWithTheme';
location.fileId = currentFile.id;
location.gistId = tempGistId;
const { gistId } = await publishSvc.publishLocationAndStore(location);
const sharePage = mainToken.providerId === 'githubAppData' ? 'gistshare.html' : 'share.html';
const url = `${window.location.protocol}//${window.location.host}/${sharePage}?id=${gistId}`;
await store.dispatch('modal/open', { type: 'shareHtml', name: currentFile.name, url });
} catch (err) {
if (err) {
store.dispatch('notification/error', err);
}
} finally {
this.sharing = false;
}
},
},
};
</script>
<style lang="scss">
@import '../styles/variables.scss';
.preview-in-page-buttons {
position: absolute;
bottom: 10px;
right: -98px;
height: 34px;
padding: 5px;
background-color: rgba(84, 96, 114, 0.4);
border-radius: $border-radius-base;
transition: 0.5s;
display: flex;
.dropdown-menu {
display: none;
}
&:active,
&:focus,
&:hover {
right: 0;
transition: 0.5s;
background-color: #546072;
.dropdown-menu {
display: block;
}
}
.dropdown-menu-items {
bottom: 100%;
top: unset;
}
ul {
padding: 0;
margin-left: 10px;
line-height: 20px;
li {
line-height: 16px;
width: 16px;
display: inline-block;
vertical-align: middle;
list-style: none;
cursor: pointer;
font-size: 14px;
margin-right: 10px;
.icon {
color: #fff;
opacity: 0.7;
&:active,
&:focus,
&:hover {
opacity: 1;
}
}
}
.before {
margin-left: -16px;
margin-right: 0;
}
}
}
</style>

View File

@ -24,7 +24,6 @@
<pre class="markdown-highlighting" v-html="markdownSample"></pre> <pre class="markdown-highlighting" v-html="markdownSample"></pre>
</div> </div>
<edit-theme-menu v-else-if="panel === 'editTheme'"></edit-theme-menu> <edit-theme-menu v-else-if="panel === 'editTheme'"></edit-theme-menu>
<preview-theme-menu v-else-if="panel === 'previewTheme'"></preview-theme-menu>
<div class="side-bar__panel side-bar__panel--toc" :class="{'side-bar__panel--hidden': panel !== 'toc'}"> <div class="side-bar__panel side-bar__panel--toc" :class="{'side-bar__panel--hidden': panel !== 'toc'}">
<toc> <toc>
</toc> </toc>
@ -44,7 +43,6 @@ import HistoryMenu from './menus/HistoryMenu';
import ImportExportMenu from './menus/ImportExportMenu'; import ImportExportMenu from './menus/ImportExportMenu';
import WorkspaceBackupMenu from './menus/WorkspaceBackupMenu'; import WorkspaceBackupMenu from './menus/WorkspaceBackupMenu';
import EditThemeMenu from './menus/EditThemeMenu'; import EditThemeMenu from './menus/EditThemeMenu';
import PreviewThemeMenu from './menus/PreviewThemeMenu';
import markdownSample from '../data/markdownSample.md'; import markdownSample from '../data/markdownSample.md';
import markdownConversionSvc from '../services/markdownConversionSvc'; import markdownConversionSvc from '../services/markdownConversionSvc';
import store from '../store'; import store from '../store';
@ -60,7 +58,6 @@ const panelNames = {
importExport: '导入/导出', importExport: '导入/导出',
workspaceBackups: '文档空间备份', workspaceBackups: '文档空间备份',
editTheme: '编辑区主题', editTheme: '编辑区主题',
previewTheme: '预览区主题',
}; };
export default { export default {
@ -74,7 +71,6 @@ export default {
ImportExportMenu, ImportExportMenu,
WorkspaceBackupMenu, WorkspaceBackupMenu,
EditThemeMenu, EditThemeMenu,
PreviewThemeMenu,
}, },
data: () => ({ data: () => ({
markdownSample: markdownConversionSvc.highlight(markdownSample), markdownSample: markdownConversionSvc.highlight(markdownSample),

View File

@ -1,9 +1,10 @@
<template> <template>
<span class="dropdown-menu"> <span class="dropdown-menu">
<span ref="slotInfo" @click="toggleMenu()" class="dropdown-toggle"> <span @click="toggleMenu()" class="dropdown-toggle">
<slot></slot> <slot></slot>
</span> </span>
<ul class="dropdown-menu-items" :style="dropdownStyle" v-if="showMenu">
<ul class="dropdown-menu-items" v-if="showMenu">
<li v-for="(option, idx) in options" :key="idx"> <li v-for="(option, idx) in options" :key="idx">
<a href="javascript:void(0)" :class="{selected: option.value === selectedOption.value}" @click="updateOption(option)"> <a href="javascript:void(0)" :class="{selected: option.value === selectedOption.value}" @click="updateOption(option)">
{{ option.name }} {{ option.name }}
@ -14,8 +15,6 @@
</template> </template>
<script> <script>
import store from '../../store';
export default { export default {
data: () => ({ data: () => ({
selectedOption: { selectedOption: {
@ -47,19 +46,13 @@
beforeDestroy() { beforeDestroy() {
document.removeEventListener('click', this.clickHandler); document.removeEventListener('click', this.clickHandler);
}, },
computed: {
dropdownStyle() {
const height = store.state.layout.bodyHeight;
return `max-height: ${height * 0.7}px;`;
},
},
methods: { methods: {
updateOption(option) { updateOption(option) {
this.selectedOption = option; this.selectedOption = option;
if (this.closeOnItemClick) { if (this.closeOnItemClick) {
this.showMenu = false; this.showMenu = false;
} }
this.$emit('change', option); this.$emit('change', this.selectedOption);
}, },
toggleMenu() { toggleMenu() {
this.showMenu = !this.showMenu; this.showMenu = !this.showMenu;
@ -91,7 +84,7 @@
max-height: 450px; max-height: 450px;
overflow-y: scroll; overflow-y: scroll;
padding: 5px 0; padding: 5px 0;
margin: 0; margin: 2px 0 0;
list-style: none; list-style: none;
font-size: 15px; font-size: 15px;
background-color: #666; background-color: #666;

View File

@ -8,7 +8,7 @@
</option> </option>
</select> </select>
</p> </p>
<p v-if="!historyContext">同步 <b>{{currentFileName}}</b> 以启用修订历史 或者 <a href="javascript:void(0)" @click="signin">登录 Gitee</a> <a href="javascript:void(0)" @click="signinWithGithub">登录 GitHub</a> 以同步您的主文档空间</p> <p v-if="!historyContext">同步 <b>{{currentFileName}}</b> 以启用修订历史 或者 <a href="javascript:void(0)" @click="signin">登录 Gitee</a> 以同步您的主文档空间</p>
<p v-else-if="loading">历史版本加载中</p> <p v-else-if="loading">历史版本加载中</p>
<p v-else-if="!revisionsWithSpacer.length"><b>{{currentFileName}}</b> 没有历史版本.</p> <p v-else-if="!revisionsWithSpacer.length"><b>{{currentFileName}}</b> 没有历史版本.</p>
<div class="menu-entry menu-entry--info flex flex--row flex--align-center" v-else> <div class="menu-entry menu-entry--info flex flex--row flex--align-center" v-else>
@ -55,7 +55,6 @@ import EditorClassApplier from '../common/EditorClassApplier';
import PreviewClassApplier from '../common/PreviewClassApplier'; import PreviewClassApplier from '../common/PreviewClassApplier';
import utils from '../../services/utils'; import utils from '../../services/utils';
import giteeHelper from '../../services/providers/helpers/giteeHelper'; import giteeHelper from '../../services/providers/helpers/giteeHelper';
import githubHelper from '../../services/providers/helpers/githubHelper';
import syncSvc from '../../services/syncSvc'; import syncSvc from '../../services/syncSvc';
import store from '../../store'; import store from '../../store';
import badgeSvc from '../../services/badgeSvc'; import badgeSvc from '../../services/badgeSvc';
@ -169,16 +168,6 @@ export default {
async signin() { async signin() {
try { try {
await giteeHelper.signin(); await giteeHelper.signin();
await syncSvc.afterSignIn();
syncSvc.requestSync();
} catch (e) {
// Cancel
}
},
async signinWithGithub() {
try {
await githubHelper.signin();
await syncSvc.afterSignIn();
syncSvc.requestSync(); syncSvc.requestSync();
} catch (e) { } catch (e) {
// Cancel // Cancel

View File

@ -14,9 +14,6 @@
<span v-if="currentWorkspace.providerId === 'giteeAppData'"> <span v-if="currentWorkspace.providerId === 'giteeAppData'">
<b>{{currentWorkspace.name}}</b> 与您的 Gitee 默认文档空间仓库同步 <b>{{currentWorkspace.name}}</b> 与您的 Gitee 默认文档空间仓库同步
</span> </span>
<span v-else-if="currentWorkspace.providerId === 'githubAppData'">
<b>{{currentWorkspace.name}}</b> 与您的 GitHub 默认文档空间仓库同步
</span>
<span v-else-if="currentWorkspace.providerId === 'googleDriveWorkspace'"> <span v-else-if="currentWorkspace.providerId === 'googleDriveWorkspace'">
<b>{{currentWorkspace.name}}</b> <a :href="workspaceLocationUrl" target="_blank">Google Drive 文件夹</a>同步 <b>{{currentWorkspace.name}}</b> <a :href="workspaceLocationUrl" target="_blank">Google Drive 文件夹</a>同步
</span> </span>
@ -24,10 +21,10 @@
<b>{{currentWorkspace.name}}</b> <a :href="workspaceLocationUrl" target="_blank">CouchDB 数据库</a>同步 <b>{{currentWorkspace.name}}</b> <a :href="workspaceLocationUrl" target="_blank">CouchDB 数据库</a>同步
</span> </span>
<span v-else-if="currentWorkspace.providerId === 'githubWorkspace'"> <span v-else-if="currentWorkspace.providerId === 'githubWorkspace'">
<b>{{currentWorkspace.name}}</b> <a :href="workspaceLocationUrl" target="_blank">GitHub 仓库</a> 同步 <b>{{currentWorkspace.name}}</b> <a :href="workspaceLocationUrl" target="_blank">GitHub repo</a> 同步
</span> </span>
<span v-else-if="currentWorkspace.providerId === 'giteeWorkspace'"> <span v-else-if="currentWorkspace.providerId === 'giteeWorkspace'">
<b>{{currentWorkspace.name}}</b> <a :href="workspaceLocationUrl" target="_blank">Gitee 仓库</a> 同步 <b>{{currentWorkspace.name}}</b> <a :href="workspaceLocationUrl" target="_blank">Gitee repo</a> 同步
</span> </span>
<span v-else-if="currentWorkspace.providerId === 'gitlabWorkspace'"> <span v-else-if="currentWorkspace.providerId === 'gitlabWorkspace'">
<b>{{currentWorkspace.name}}</b> <a :href="workspaceLocationUrl" target="_blank">GitLab 项目</a>同步 <b>{{currentWorkspace.name}}</b> <a :href="workspaceLocationUrl" target="_blank">GitLab 项目</a>同步
@ -48,11 +45,6 @@
<div>使用 Gitee 登录</div> <div>使用 Gitee 登录</div>
<span>同步您的主文档空间并解锁功能</span> <span>同步您的主文档空间并解锁功能</span>
</menu-entry> </menu-entry>
<menu-entry v-if="!loginToken" @click.native="signinWithGithub">
<icon-login slot="icon"></icon-login>
<div>使用 GitHub 登录</div>
<span>同步您的主文档空间并解锁功能</span>
</menu-entry>
<menu-entry @click.native="setPanel('workspaces')"> <menu-entry @click.native="setPanel('workspaces')">
<icon-database slot="icon"></icon-database> <icon-database slot="icon"></icon-database>
<div><div class="menu-entry__label menu-entry__label--count" v-if="workspaceCount">{{workspaceCount}}</div> 文档空间</div> <div><div class="menu-entry__label menu-entry__label--count" v-if="workspaceCount">{{workspaceCount}}</div> 文档空间</div>
@ -118,11 +110,6 @@
编辑区主题 编辑区主题
<span>编辑区主题样式(自定义主题可编辑)</span> <span>编辑区主题样式(自定义主题可编辑)</span>
</menu-entry> </menu-entry>
<menu-entry @click.native="setPanel('previewTheme')">
<icon-select-theme slot="icon"></icon-select-theme>
预览区主题
<span>预览区主题样式(自定义主题可编辑)</span>
</menu-entry>
<menu-entry @click.native="settings"> <menu-entry @click.native="settings">
<icon-settings slot="icon"></icon-settings> <icon-settings slot="icon"></icon-settings>
<div>配置</div> <div>配置</div>
@ -150,7 +137,6 @@ import MenuEntry from './common/MenuEntry';
import providerRegistry from '../../services/providers/common/providerRegistry'; import providerRegistry from '../../services/providers/common/providerRegistry';
import UserImage from '../UserImage'; import UserImage from '../UserImage';
import giteeHelper from '../../services/providers/helpers/giteeHelper'; import giteeHelper from '../../services/providers/helpers/giteeHelper';
import githubHelper from '../../services/providers/helpers/githubHelper';
import syncSvc from '../../services/syncSvc'; import syncSvc from '../../services/syncSvc';
import userSvc from '../../services/userSvc'; import userSvc from '../../services/userSvc';
import store from '../../store'; import store from '../../store';
@ -203,16 +189,6 @@ export default {
async signin() { async signin() {
try { try {
await giteeHelper.signin(); await giteeHelper.signin();
await syncSvc.afterSignIn();
syncSvc.requestSync();
} catch (e) {
// Cancel
}
},
async signinWithGithub() {
try {
await githubHelper.signin();
await syncSvc.afterSignIn();
syncSvc.requestSync(); syncSvc.requestSync();
} catch (e) { } catch (e) {
// Cancel // Cancel

View File

@ -1,116 +0,0 @@
<template>
<div class="preview-theme side-bar__panel side-bar__panel--menu">
<div class="side-bar__info">
<div class="menu-entry menu-entry--info flex flex--row flex--align-center">
<span v-if="currPreviewTheme==='custom'">
下面的自定义主题样式可编辑可参考其他主题样式填入自己喜欢的预览样式<br>
主题class为preview-theme--custom
</span>
<span v-else>
下面的主题样式不可编辑
</span>
</div>
</div>
<div class="side-bar__content">
<template v-if="currPreviewTheme === 'default'">
默认主题无额外样式请选择其他主题
</template>
<template v-else>
<code-editor v-for="(value, index) in styleEles" :key="index"
v-if="value.id === `preview-theme-${currPreviewTheme}`" lang="css" :value="value.innerHTML"
:disabled="value.id!=='preview-theme-custom'" @changed="changeText" scrollClass="side-bar__inner"></code-editor>
</template>
</div>
<div class="flex flex--row flex--end" v-if="currPreviewTheme==='custom'">
<button class="preview-theme__button button" @click="saveStyleText">保存</button>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import MenuEntry from './common/MenuEntry';
import CodeEditor from '../CodeEditor';
import store from '../../store';
export default {
components: {
MenuEntry,
CodeEditor,
},
data: () => ({
themeStyleText: '',
styleEles: [],
}),
computed: {
...mapGetters('theme', [
'currPreviewTheme',
]),
},
methods: {
saveStyleText() {
const typeEle = this.findByTheme(this.currPreviewTheme);
if (!typeEle || !this.themeStyleText) {
return;
}
typeEle.innerHTML = this.themeStyleText;
store.dispatch('theme/setCustomPreviewThemeStyle', this.themeStyleText);
store.dispatch('notification/info', '保存自定义主题样式成功!');
},
findByTheme(theme) {
const findEles = this.styleEles.filter(it => it.id === `preview-theme-${theme}`);
return findEles.length ? findEles[0] : null;
},
changeText(text) {
this.themeStyleText = text;
},
close() {
store.dispatch('data/setSideBarPanel', 'menu');
},
initStyle(theme) {
if (theme === 'default') {
return;
}
const value = theme || this.currPreviewTheme;
if (this.findByTheme(value)) {
return;
}
const styleId = `preview-theme-${value}`;
const styleEle = document.getElementById(styleId);
if (!styleEle) {
setTimeout(() => this.initStyle(value), 1000);
return;
}
this.styleEles.push(styleEle);
},
},
watch: {
currPreviewTheme: {
immediate: true,
handler(val) {
this.initStyle(val);
},
},
},
created() {
this.initStyle();
},
};
</script>
<style lang="scss">
.side-bar__panel--menu {
.side-bar__content {
.code-editor {
min-height: 400px !important;
max-height: 100%;
}
}
.preview-theme__button {
font-size: 14px;
margin-top: 0.5em;
}
}
</style>

View File

@ -43,7 +43,7 @@
<div v-for="token in githubTokens" :key="token.sub"> <div v-for="token in githubTokens" :key="token.sub">
<menu-entry @click.native="publishGist(token)"> <menu-entry @click.native="publishGist(token)">
<icon-provider slot="icon" provider-id="gist"></icon-provider> <icon-provider slot="icon" provider-id="gist"></icon-provider>
<div>发布到 GitHubGist</div> <div>发布到 Gist</div>
<span>{{token.name}}</span> <span>{{token.name}}</span>
</menu-entry> </menu-entry>
<menu-entry @click.native="publishGithub(token)"> <menu-entry @click.native="publishGithub(token)">
@ -53,11 +53,6 @@
</menu-entry> </menu-entry>
</div> </div>
<div v-for="token in giteeTokens" :key="token.sub"> <div v-for="token in giteeTokens" :key="token.sub">
<menu-entry @click.native="publishGiteeGist(token)">
<icon-provider slot="icon" provider-id="giteegist"></icon-provider>
<div>发布到 GiteeGist</div>
<span>{{token.name}}</span>
</menu-entry>
<menu-entry @click.native="publishGitee(token)"> <menu-entry @click.native="publishGitee(token)">
<icon-provider slot="icon" provider-id="gitee"></icon-provider> <icon-provider slot="icon" provider-id="gitee"></icon-provider>
<div>发布到 Gitee</div> <div>发布到 Gitee</div>
@ -263,8 +258,8 @@ export default {
}, },
async addGitlabAccount() { async addGitlabAccount() {
try { try {
const { serverUrl, applicationId, applicationSecret } = await store.dispatch('modal/open', { type: 'gitlabAccount' }); const { serverUrl, applicationId } = await store.dispatch('modal/open', { type: 'gitlabAccount' });
await gitlabHelper.addAccount(serverUrl, applicationId, applicationSecret); await gitlabHelper.addAccount(serverUrl, applicationId);
} catch (e) { /* cancel */ } } catch (e) { /* cancel */ }
}, },
async addGiteaAccount() { async addGiteaAccount() {
@ -294,9 +289,8 @@ export default {
publishBloggerPage: publishModalOpener('bloggerPagePublish', 'publishToBloggerPage'), publishBloggerPage: publishModalOpener('bloggerPagePublish', 'publishToBloggerPage'),
publishDropbox: publishModalOpener('dropboxPublish', 'publishToDropbox'), publishDropbox: publishModalOpener('dropboxPublish', 'publishToDropbox'),
publishGithub: publishModalOpener('githubPublish', 'publishToGithub'), publishGithub: publishModalOpener('githubPublish', 'publishToGithub'),
publishGist: publishModalOpener('gistPublish', 'publishToGist'),
publishGitee: publishModalOpener('giteePublish', 'publishToGitee'), publishGitee: publishModalOpener('giteePublish', 'publishToGitee'),
publishGiteeGist: publishModalOpener('giteeGistPublish', 'publishGiteeGist'), publishGist: publishModalOpener('gistPublish', 'publishToGist'),
publishGitlab: publishModalOpener('gitlabPublish', 'publishToGitlab'), publishGitlab: publishModalOpener('gitlabPublish', 'publishToGitlab'),
publishGitea: publishModalOpener('giteaPublish', 'publishToGitea'), publishGitea: publishModalOpener('giteaPublish', 'publishToGitea'),
publishGoogleDrive: publishModalOpener('googleDrivePublish', 'publishToGoogleDrive'), publishGoogleDrive: publishModalOpener('googleDrivePublish', 'publishToGoogleDrive'),

View File

@ -46,7 +46,7 @@
</menu-entry> </menu-entry>
<menu-entry @click.native="saveGist(token)"> <menu-entry @click.native="saveGist(token)">
<icon-provider slot="icon" provider-id="gist"></icon-provider> <icon-provider slot="icon" provider-id="gist"></icon-provider>
<div>在GitHubGist上保存</div> <div>在Gist上保存</div>
<span>{{token.name}}</span> <span>{{token.name}}</span>
</menu-entry> </menu-entry>
</div> </div>
@ -61,11 +61,6 @@
<div>在Gitee上保存</div> <div>在Gitee上保存</div>
<span>{{token.name}}</span> <span>{{token.name}}</span>
</menu-entry> </menu-entry>
<menu-entry @click.native="saveGiteeGist(token)">
<icon-provider slot="icon" provider-id="giteegist"></icon-provider>
<div>在GiteeGist上保存</div>
<span>{{token.name}}</span>
</menu-entry>
</div> </div>
<div v-for="token in gitlabTokens" :key="token.sub"> <div v-for="token in gitlabTokens" :key="token.sub">
<menu-entry @click.native="openGitlab(token)"> <menu-entry @click.native="openGitlab(token)">
@ -239,8 +234,8 @@ export default {
}, },
async addGitlabAccount() { async addGitlabAccount() {
try { try {
const { serverUrl, applicationId, applicationSecret } = await store.dispatch('modal/open', { type: 'gitlabAccount' }); const { serverUrl, applicationId } = await store.dispatch('modal/open', { type: 'gitlabAccount' });
await gitlabHelper.addAccount(serverUrl, applicationId, applicationSecret); await gitlabHelper.addAccount(serverUrl, applicationId);
} catch (e) { /* cancel */ } } catch (e) { /* cancel */ }
}, },
async addGiteaAccount() { async addGiteaAccount() {
@ -335,12 +330,6 @@ export default {
badgeSvc.addBadge('saveOnGist'); badgeSvc.addBadge('saveOnGist');
} catch (e) { /* cancel */ } } catch (e) { /* cancel */ }
}, },
async saveGiteeGist(token) {
try {
await openSyncModal(token, 'giteeGistSync');
badgeSvc.addBadge('saveOnGiteeGist');
} catch (e) { /* cancel */ }
},
async openGitlab(token) { async openGitlab(token) {
try { try {
const syncLocation = await store.dispatch('modal/open', { const syncLocation = await store.dispatch('modal/open', {

View File

@ -8,8 +8,7 @@
<hr> <hr>
<div class="workspace" v-for="(workspace, id) in workspacesById" :key="id"> <div class="workspace" v-for="(workspace, id) in workspacesById" :key="id">
<menu-entry :href="workspace.url" target="_blank"> <menu-entry :href="workspace.url" target="_blank">
<icon-provider v-if="id === 'main' && !workspace.sub" slot="icon" :provider-id="'stackedit'"></icon-provider> <icon-provider slot="icon" :provider-id="workspace.providerId"></icon-provider>
<icon-provider v-else slot="icon" :provider-id="workspace.providerId"></icon-provider>
<div class="workspace__name"><div class="menu-entry__label" v-if="currentWorkspace === workspace">当前</div>{{workspace.name}}</div> <div class="workspace__name"><div class="menu-entry__label" v-if="currentWorkspace === workspace">当前</div>{{workspace.name}}</div>
</menu-entry> </menu-entry>
</div> </div>
@ -86,8 +85,8 @@ export default {
}, },
async addGitlabWorkspace() { async addGitlabWorkspace() {
try { try {
const { serverUrl, applicationId, applicationSecret } = await store.dispatch('modal/open', { type: 'gitlabAccount' }); const { serverUrl, applicationId } = await store.dispatch('modal/open', { type: 'gitlabAccount' });
const token = await gitlabHelper.addAccount(serverUrl, applicationId, applicationSecret); const token = await gitlabHelper.addAccount(serverUrl, applicationId);
store.dispatch('modal/open', { store.dispatch('modal/open', {
type: 'gitlabWorkspace', type: 'gitlabWorkspace',
token, token,

View File

@ -248,8 +248,8 @@ export default {
}, },
async addGitlabAccount() { async addGitlabAccount() {
try { try {
const { serverUrl, applicationId, applicationSecret } = await store.dispatch('modal/open', { type: 'gitlabAccount' }); const { serverUrl, applicationId } = await store.dispatch('modal/open', { type: 'gitlabAccount' });
await gitlabHelper.addAccount(serverUrl, applicationId, applicationSecret); await gitlabHelper.addAccount(serverUrl, applicationId);
} catch (e) { /* cancel */ } } catch (e) { /* cancel */ }
}, },
async addGiteaAccount() { async addGiteaAccount() {

View File

@ -1,131 +0,0 @@
<template>
<modal-inner class="modal__inner-1--chatgpt" aria-label="chatgpt">
<div class="modal__content">
<div class="modal__image">
<icon-chat-gpt></icon-chat-gpt>
</div>
<p><b>ChatGPT内容生成</b><br>生成时长受ChatGPT服务响应与网络响应时长影响时间可能较长</p>
<form-entry label="生成内容要求详细描述" error="content">
<textarea slot="field" class="text-input" type="text" placeholder="输入内容(支持换行)" v-model.trim="content" :disabled="generating"></textarea>
<div class="form-entry__info">
使用 <a href="https://api35.pxj123.cn/" target="_blank">api35.pxj123.cn</a> 的免费接口生成内容AI模型是GPT-3.5 Turbo
</div>
</form-entry>
<div class="modal__result">
<pre class="result_pre" v-if="generating && !result">(等待生成中...)</pre>
<pre class="result_pre" v-else v-text="result"></pre>
</div>
</div>
<div class="modal__button-bar">
<button class="button" @click="reject()">{{ generating ? '停止' : '关闭' }}</button>
<button class="button button--resolve" @click="generate" v-if="!generating && !!content">{{ !!result ? '重新生成' : '开始生成' }}</button>
<button class="button button--resolve" @click="resolve" v-if="!generating && !!result">确认插入</button>
</div>
</modal-inner>
</template>
<script>
import modalTemplate from './common/modalTemplate';
import chatGptSvc from '../../services/chatGptSvc';
import store from '../../store';
export default modalTemplate({
data: () => ({
generating: false,
content: '',
result: '',
xhr: null,
}),
methods: {
resolve(evt) {
evt.preventDefault();
const { callback } = this.config;
this.config.resolve();
callback(this.result);
},
process({ done, content, error }) {
if (done) {
this.generating = false;
//
} else if (content) {
this.result = this.result + content;
const container = document.querySelector('.result_pre');
container.scrollTo(0, container.scrollHeight); //
} else if (error) {
this.generating = false;
}
},
generate() {
this.generating = true;
this.result = '';
try {
this.xhr = chatGptSvc.chat({
content: `${this.content}\n(使用Markdown方式输出结果)`,
}, this.process);
} catch (err) {
this.generating = false;
store.dispatch('notification/error', err);
}
},
reject() {
if (this.generating) {
if (this.xhr) {
this.xhr.abort();
this.generating = false;
}
return;
}
const { callback } = this.config;
this.config.reject();
callback(null);
},
},
mounted() {
const script = document.createElement('script');
script.src = `https://api35.pxj123.cn/js/chat.js?t=${new Date().getTime()}`;
script.onload = () => {
/* eslint-disable */
console.log('加载外部chatgpt的js成功!');
};
this.$el.appendChild(script);
},
});
</script>
<style lang="scss">
@import '../../styles/variables.scss';
.modal__inner-1.modal__inner-1--chatgpt {
max-width: 560px;
.result_pre {
font-size: 0.9em;
font-variant-ligatures: no-common-ligatures;
line-height: 1.25;
white-space: pre-wrap;
word-break: break-word;
word-wrap: break-word;
height: 300px;
border: 1px solid rgb(126, 126, 126);
border-radius: $border-radius-base;
padding: 10px;
overflow-y: scroll; /* 开启垂直滚动条 */
}
.result_pre::-webkit-scrollbar {
display: none; /* 隐藏滚动条 */
}
.result_pre.scroll-bottom {
scroll-behavior: smooth;
}
.config-warning {
color: #f00;
}
.text-input {
min-height: 60px;
}
}
</style>

View File

@ -2,12 +2,9 @@
<modal-inner aria-label="提交信息"> <modal-inner aria-label="提交信息">
<p>自定义 <b>{{ config.name }}</b> 提交信息</p> <p>自定义 <b>{{ config.name }}</b> 提交信息</p>
<div class="modal__content"> <div class="modal__content">
<div class="form-entry"> <form-entry label="提交信息">
<label class="form-entry__label">提交信息</label>
<div class="form-entry__field">
<input slot="field" class="textfield" placeholder="提交信息非必填" type="text" v-model.trim="commitMessage" @keydown.enter="resolve()"> <input slot="field" class="textfield" placeholder="提交信息非必填" type="text" v-model.trim="commitMessage" @keydown.enter="resolve()">
</div> </form-entry>
</div>
</div> </div>
<div class="modal__button-bar"> <div class="modal__button-bar">
<button class="button" @click="config.reject()">取消</button> <button class="button" @click="config.reject()">取消</button>

View File

@ -22,7 +22,7 @@
<menu-item> <menu-item>
<icon-provider slot="icon" :provider-id="currentWorkspace.providerId"></icon-provider> <icon-provider slot="icon" :provider-id="currentWorkspace.providerId"></icon-provider>
<div> <div>
当前文档空间图片路径 文档空间图片路径
<button class="menu-item__button button" @click.stop="removeByPath(path)" v-title="'删除'"> <button class="menu-item__button button" @click.stop="removeByPath(path)" v-title="'删除'">
<icon-delete></icon-delete> <icon-delete></icon-delete>
</button> </button>
@ -62,7 +62,7 @@
</menu-entry> </menu-entry>
<menu-entry @click.native="addWorkspaceImgPath"> <menu-entry @click.native="addWorkspaceImgPath">
<icon-provider slot="icon" :provider-id="currentWorkspace.providerId"></icon-provider> <icon-provider slot="icon" :provider-id="currentWorkspace.providerId"></icon-provider>
<span>添加当前文档空间图片路径</span> <span>添加文档空间图片路径</span>
</menu-entry> </menu-entry>
<menu-entry @click.native="addSmmsAccount"> <menu-entry @click.native="addSmmsAccount">
<icon-provider slot="icon" provider-id="smms"></icon-provider> <icon-provider slot="icon" provider-id="smms"></icon-provider>
@ -109,15 +109,11 @@ export default modalTemplate({
computed: { computed: {
...mapGetters('workspace', [ ...mapGetters('workspace', [
'currentWorkspace', 'currentWorkspace',
'currentWorkspaceIsGit',
]), ]),
checkedStorage() { checkedStorage() {
return store.getters['img/getCheckedStorage']; return store.getters['img/getCheckedStorage'];
}, },
workspaceImgPath() { workspaceImgPath() {
if (!this.currentWorkspaceIsGit) {
return [];
}
const workspaceImgPath = store.getters['img/getWorkspaceImgPath']; const workspaceImgPath = store.getters['img/getWorkspaceImgPath'];
return Object.keys(workspaceImgPath || {}); return Object.keys(workspaceImgPath || {});
}, },

View File

@ -1,7 +1,7 @@
<template> <template>
<modal-inner aria-label="导出到PDF"> <modal-inner aria-label="导出到PDF">
<div class="modal__content"> <div class="modal__content">
<p>请为您的<b> pdf导出</b>选择模板(该导出很消耗服务器资源文档太大或图片太多可能会导出超时失败可参考 <a href="https://gitee.com/mafgwo/stackedit/blob/master/docs/大文档导出PDF方式.md" target="_blank">大文档导出PDF方式</a> 自行导出大文档)</p> <p>请为您的<b> pdf导出</b>选择模板</p>
<form-entry label="模板"> <form-entry label="模板">
<select class="textfield" slot="field" v-model="selectedTemplate" @keydown.enter="resolve()"> <select class="textfield" slot="field" v-model="selectedTemplate" @keydown.enter="resolve()">
<option v-for="(template, id) in allTemplatesById" :key="id" :value="id"> <option v-for="(template, id) in allTemplatesById" :key="id" :value="id">

View File

@ -4,8 +4,8 @@
<div class="modal__image"> <div class="modal__image">
<icon-upload></icon-upload> <icon-upload></icon-upload>
</div> </div>
<p v-if="publishLocations.length"><b>{{currentFileName}}</b> 被发布到了以下位置:</p> <p v-if="publishLocations.length"><b>{{currentFileName}}</b> is published to the following location(s):</p>
<p v-else><b>{{currentFileName}}</b> 还没有被发布.</p> <p v-else><b>{{currentFileName}}</b> is not published yet.</p>
<div> <div>
<div class="publish-entry flex flex--column" v-for="location in publishLocations" :key="location.id"> <div class="publish-entry flex flex--column" v-for="location in publishLocations" :key="location.id">
<div class="publish-entry__header flex flex--row flex--align-center"> <div class="publish-entry__header flex flex--row flex--align-center">
@ -26,7 +26,7 @@
{{location.url}} {{location.url}}
</div> </div>
<div class="publish-entry__buttons flex flex--row flex--center" v-if="location.url"> <div class="publish-entry__buttons flex flex--row flex--center" v-if="location.url">
<button class="publish-entry__button button" v-clipboard="location.url" @click="info('位置URL已复制到剪贴板!')" v-title="'复制URL'"> <button class="publish-entry__button button" v-clipboard="location.url" @click="info('Location URL copied to clipboard!')" v-title="'复制URL'">
<icon-content-copy></icon-content-copy> <icon-content-copy></icon-content-copy>
</button> </button>
<a class="publish-entry__button button" v-if="location.url" :href="location.url" target="_blank" v-title="'打开位置'"> <a class="publish-entry__button button" v-if="location.url" :href="location.url" target="_blank" v-title="'打开位置'">
@ -34,19 +34,6 @@
</a> </a>
</div> </div>
</div> </div>
<div class="publish-entry__row flex flex--row flex--align-center" v-if="shareUrl(location)">
<div class="publish-entry__url">
分享链接: {{shareUrl(location)}}
</div>
<div class="publish-entry__buttons flex flex--row flex--center">
<button class="publish-entry__button button" v-clipboard="shareUrl(location)" @click="info('分享URL已复制到剪贴板!')" v-title="'复制分享URL'">
<icon-content-copy></icon-content-copy>
</button>
<a class="publish-entry__button button" :href="shareUrl(location)" target="_blank" v-title="'打开分享'">
<icon-open-in-new></icon-open-in-new>
</a>
</div>
</div>
</div> </div>
</div> </div>
<div class="modal__info" v-if="publishLocations.length"> <div class="modal__info" v-if="publishLocations.length">
@ -88,16 +75,6 @@ export default {
store.commit('publishLocation/deleteItem', location.id); store.commit('publishLocation/deleteItem', location.id);
badgeSvc.addBadge('removePublishLocation'); badgeSvc.addBadge('removePublishLocation');
}, },
shareUrl(location) {
if (location.providerId !== 'giteegist' && location.providerId !== 'gist') {
return null;
}
if (!location.url || !location.gistId) {
return null;
}
const sharePage = location.providerId === 'gist' ? 'gistshare.html' : 'share.html';
return `${window.location.protocol}//${window.location.host}/${sharePage}?id=${location.gistId}`;
},
}, },
}; };
</script> </script>

View File

@ -8,9 +8,8 @@
<form-entry label="图片上传路径" error="path"> <form-entry label="图片上传路径" error="path">
<input slot="field" class="textfield" type="text" placeholder="如:/imgs/{YYYY}-{MM}-{DD}" v-model.trim="path" @keydown.enter="resolve()"> <input slot="field" class="textfield" type="text" placeholder="如:/imgs/{YYYY}-{MM}-{DD}" v-model.trim="path" @keydown.enter="resolve()">
<div class="form-entry__info"> <div class="form-entry__info">
如果不提供默认为 /imgs/{YYYY}-{MM}-{DD}<br/> 如果不提供默认为 /imgs/{YYYY}-{MM}-{DD} 其中{YYYY}为年变量{MM}为月变量{DD}为日变量<br/>
支持相对路径 ./imgs../imgs imgs 都是相对当前编辑中文档的路径<br/> 支持相对路径 ./imgs imgs 都是相对当前编辑中文档的路径不支持相对上级路径
变量说明{YYYY}为年变量{MM}为月变量{DD}为日变量{MDNAME}为当前文档名称
</div> </div>
</form-entry> </form-entry>
</div> </div>
@ -36,11 +35,9 @@ export default modalTemplate({
}, },
methods: { methods: {
resolve() { resolve() {
if (!this.path) { const path = this.path && this.path.replace(/^\//, '');
this.setError('path');
}
this.config.resolve({ this.config.resolve({
path: this.path || '/imgs/{YYYY}-{MM}-{DD}', path: path || '/imgs/{YYYY}-{MM}-{DD}',
}); });
}, },
}, },

View File

@ -9,8 +9,7 @@
<div class="flex flex--column"> <div class="flex flex--column">
<div class="workspace-entry__header flex flex--row flex--align-center"> <div class="workspace-entry__header flex flex--row flex--align-center">
<div class="workspace-entry__icon"> <div class="workspace-entry__icon">
<icon-provider v-if="id === 'main' && !workspace.sub" :provider-id="'stackedit'"></icon-provider> <icon-provider :provider-id="workspace.providerId"></icon-provider>
<icon-provider v-else :provider-id="workspace.providerId"></icon-provider>
</div> </div>
<input class="text-input" type="text" v-if="editedId === id" v-focus @blur="submitEdit()" @keydown.enter="submitEdit()" @keydown.esc.stop="submitEdit(true)" v-model="editingName"> <input class="text-input" type="text" v-if="editedId === id" v-focus @blur="submitEdit()" @keydown.enter="submitEdit()" @keydown.esc.stop="submitEdit(true)" v-model="editingName">
<div class="workspace-entry__name" v-else>{{workspace.name}}</div> <div class="workspace-entry__name" v-else>{{workspace.name}}</div>
@ -18,7 +17,7 @@
<button class="workspace-entry__button button" @click="edit(id)" v-title="'编辑名称'"> <button class="workspace-entry__button button" @click="edit(id)" v-title="'编辑名称'">
<icon-pen></icon-pen> <icon-pen></icon-pen>
</button> </button>
<template v-if="workspace.providerId === 'giteeAppData' || workspace.providerId === 'githubAppData' || workspace.providerId === 'githubWorkspace' <template v-if="workspace.providerId === 'giteeAppData' || workspace.providerId === 'githubWorkspace'
|| workspace.providerId === 'giteeWorkspace' || workspace.providerId === 'gitlabWorkspace' || workspace.providerId === 'giteaWorkspace'"> || workspace.providerId === 'giteeWorkspace' || workspace.providerId === 'gitlabWorkspace' || workspace.providerId === 'giteaWorkspace'">
<button class="workspace-entry__button button" @click="stopAutoSync(id)" v-if="workspace.autoSync == undefined || workspace.autoSync" v-title="'关闭自动同步'"> <button class="workspace-entry__button button" @click="stopAutoSync(id)" v-if="workspace.autoSync == undefined || workspace.autoSync" v-title="'关闭自动同步'">
<icon-sync-auto></icon-sync-auto> <icon-sync-auto></icon-sync-auto>

View File

@ -17,7 +17,7 @@ export default {
uid: utils.uid(), uid: utils.uid(),
}), }),
mounted() { mounted() {
this.$el.querySelector('input,select,textarea').id = this.uid; this.$el.querySelector('input,select').id = this.uid;
}, },
}; };
</script> </script>

View File

@ -1,11 +1,11 @@
<template> <template>
<modal-inner aria-label="发布到GitHubGist"> <modal-inner aria-label="发布到Gist">
<div class="modal__content"> <div class="modal__content">
<div class="modal__image"> <div class="modal__image">
<icon-provider provider-id="gist"></icon-provider> <icon-provider provider-id="gist"></icon-provider>
</div> </div>
<p>发布<b> {{CurrentFileName}} </b><b>GitHubGist</b></p> <p>发布<b> {{CurrentFileName}} </b><b>Gist</b></p>
<form-entry label="文件名" error="filename"> <form-entry label="Filename" error="filename">
<input slot="field" class="textfield" type="text" v-model.trim="filename" @keydown.enter="resolve()"> <input slot="field" class="textfield" type="text" v-model.trim="filename" @keydown.enter="resolve()">
</form-entry> </form-entry>
<div class="form-entry"> <div class="form-entry">
@ -15,10 +15,10 @@
</label> </label>
</div> </div>
</div> </div>
<form-entry label="存在Gist ID" info="可选的"> <form-entry label="Existing Gist ID" info="可选的">
<input slot="field" class="textfield" type="text" v-model.trim="gistId" @keydown.enter="resolve()"> <input slot="field" class="textfield" type="text" v-model.trim="gistId" @keydown.enter="resolve()">
<div class="form-entry__info"> <div class="form-entry__info">
如果文件存在于GitHubGist中则将被覆盖 如果文件存在于Gist中则将被覆盖
</div> </div>
</form-entry> </form-entry>
<form-entry label="Template"> <form-entry label="Template">

View File

@ -1,11 +1,11 @@
<template> <template>
<modal-inner aria-label=" GitHubGist 同步"> <modal-inner aria-label=" Gist 同步">
<div class="modal__content"> <div class="modal__content">
<div class="modal__image"> <div class="modal__image">
<icon-provider provider-id="gist"></icon-provider> <icon-provider provider-id="gist"></icon-provider>
</div> </div>
<p><b> {{currentFileName}} </b>保存到<b>GitHubGist</b>并保持同步</p> <p><b> {{currentFileName}} </b>保存到<b>Gist</b>并保持同步</p>
<form-entry label="文件名" error="filename"> <form-entry label="Filename" error="filename">
<input slot="field" class="textfield" type="text" v-model.trim="filename" @keydown.enter="resolve()"> <input slot="field" class="textfield" type="text" v-model.trim="filename" @keydown.enter="resolve()">
</form-entry> </form-entry>
<div class="form-entry"> <div class="form-entry">
@ -15,10 +15,10 @@
</label> </label>
</div> </div>
</div> </div>
<form-entry label="存在Gist ID" info="可选的"> <form-entry label="Existing Gist ID" info="可选的">
<input slot="field" class="textfield" type="text" v-model.trim="gistId" @keydown.enter="resolve()"> <input slot="field" class="textfield" type="text" v-model.trim="gistId" @keydown.enter="resolve()">
<div class="form-entry__info"> <div class="form-entry__info">
如果文件存在于GitHubGist中则将被覆盖 如果文件存在于Gist中则将被覆盖
</div> </div>
</form-entry> </form-entry>
</div> </div>

View File

@ -41,7 +41,6 @@
import modalTemplate from '../common/modalTemplate'; import modalTemplate from '../common/modalTemplate';
import constants from '../../../data/constants'; import constants from '../../../data/constants';
import store from '../../../store'; import store from '../../../store';
import networkSvc from '../../../services/networkSvc';
export default modalTemplate({ export default modalTemplate({
data: () => ({ data: () => ({
@ -66,9 +65,6 @@ export default modalTemplate({
return !!confClientId && !!confServerUrl; return !!confClientId && !!confServerUrl;
}, },
}, },
mounted() {
networkSvc.getServerConf();
},
methods: { methods: {
resolve() { resolve() {
if (this.useServerConf) { if (this.useServerConf) {

View File

@ -12,10 +12,9 @@
</div> </div>
</form-entry> </form-entry>
<form-entry label="文件夹路径" info="可选的"> <form-entry label="文件夹路径" info="可选的">
<input slot="field" class="textfield" type="text" placeholder="如imgs/{YYYY}-{MM}-{DD}" v-model.trim="path" @keydown.enter="resolve()"> <input slot="field" class="textfield" type="text" placeholder="如imgs/{YYYY}/{MM}" v-model.trim="path" @keydown.enter="resolve()">
<div class="form-entry__info"> <div class="form-entry__info">
如果不提供默认为 imgs/{YYYY}-{MM}-{DD} <br/> 如果不提供默认为 {YYYY}/{MM}/{DD} 其中{YYYY}为年变量{MM}为月变量{DD}为日变量
变量说明{YYYY}为年变量{MM}为月变量{DD}为日变量{MDNAME}为当前文档名称
</div> </div>
</form-entry> </form-entry>
<form-entry label="分支" info="可选的"> <form-entry label="分支" info="可选的">
@ -63,7 +62,7 @@ export default modalTemplate({
const path = this.path && this.path.replace(/^\//, ''); const path = this.path && this.path.replace(/^\//, '');
this.config.resolve({ this.config.resolve({
repoUri: projectPath, repoUri: projectPath,
path: path || 'imgs/{YYYY}-{MM}-{DD}', path: path || '{YYYY}/{MM}/{DD}',
branch: this.branch || 'master', branch: this.branch || 'master',
}); });
} catch (err) { } catch (err) {

View File

@ -4,7 +4,7 @@
<div class="modal__image"> <div class="modal__image">
<icon-provider provider-id="gitea"></icon-provider> <icon-provider provider-id="gitea"></icon-provider>
</div> </div>
<p>向您的<b> Gitea </b>项目发布<b> {{ currentFileName }} </b></p> <p>向您的<b> Gitea </b>项目发布<b> {{CurrentFileName}} </b></p>
<form-entry label="Project URL" error="projectUrl"> <form-entry label="Project URL" error="projectUrl">
<input slot="field" class="textfield" type="text" v-model.trim="projectUrl" @keydown.enter="resolve()"> <input slot="field" class="textfield" type="text" v-model.trim="projectUrl" @keydown.enter="resolve()">
<div class="form-entry__info"> <div class="form-entry__info">

View File

@ -1,79 +0,0 @@
<template>
<modal-inner aria-label="发布到GiteeGist">
<div class="modal__content">
<div class="modal__image">
<icon-provider provider-id="giteegist"></icon-provider>
</div>
<p>发布<b> {{CurrentFileName}} </b><b>GiteeGist</b></p>
<form-entry label="文件名" error="filename">
<input slot="field" class="textfield" type="text" v-model.trim="filename" @keydown.enter="resolve()">
</form-entry>
<div class="form-entry">
<div class="form-entry__checkbox">
<label>
<input type="checkbox" v-model="isPublic"> 公开的
</label>
</div>
</div>
<form-entry label="存在Gist ID" info="可选的">
<input slot="field" class="textfield" type="text" v-model.trim="gistId" @keydown.enter="resolve()">
<div class="form-entry__info">
如果文件存在于GiteeGist中则将被覆盖
</div>
</form-entry>
<form-entry label="Template">
<select slot="field" class="textfield" v-model="selectedTemplate" @keydown.enter="resolve()">
<option v-for="(template, id) in allTemplatesById" :key="id" :value="id">
{{ template.name }}
</option>
</select>
<div class="form-entry__actions">
<a href="javascript:void(0)" @click="configureTemplates">配置模板</a>
</div>
</form-entry>
<div class="modal__info">
<b>ProTip:</b> You can provide a value for <code>title</code> in the <a href="javascript:void(0)" @click="openFileProperties">file properties</a>.
</div>
</div>
<div class="modal__button-bar">
<button class="button" @click="config.reject()">取消</button>
<button class="button button--resolve" @click="resolve()">确认</button>
</div>
</modal-inner>
</template>
<script>
import giteeGistProvider from '../../../services/providers/giteeGistProvider';
import modalTemplate from '../common/modalTemplate';
export default modalTemplate({
data: () => ({
filename: '',
gistId: '',
}),
computedLocalSettings: {
isPublic: 'gistIsPublic',
selectedTemplate: 'gistPublishTemplate',
},
created() {
this.filename = `${this.currentFileName}.md`;
},
methods: {
resolve() {
if (!this.filename) {
this.setError('filename');
} else {
// Return new location
const location = giteeGistProvider.makeLocation(
this.config.token,
this.filename,
this.isPublic,
this.gistId,
);
location.templateId = this.selectedTemplate;
this.config.resolve(location);
}
},
},
});
</script>

View File

@ -1,64 +0,0 @@
<template>
<modal-inner aria-label=" GiteeGist 同步">
<div class="modal__content">
<div class="modal__image">
<icon-provider provider-id="giteegist"></icon-provider>
</div>
<p><b> {{currentFileName}} </b>保存到<b>GiteeGist</b>并保持同步</p>
<form-entry label="文件名" error="filename">
<input slot="field" class="textfield" type="text" v-model.trim="filename" @keydown.enter="resolve()">
</form-entry>
<div class="form-entry">
<div class="form-entry__checkbox">
<label>
<input type="checkbox" v-model="isPublic"> 公开的
</label>
</div>
</div>
<form-entry label="存在Gist ID" info="可选的">
<input slot="field" class="textfield" type="text" v-model.trim="gistId" @keydown.enter="resolve()">
<div class="form-entry__info">
如果文件存在于GiteeGist中则将被覆盖
</div>
</form-entry>
</div>
<div class="modal__button-bar">
<button class="button" @click="config.reject()">取消</button>
<button class="button button--resolve" @click="resolve()">确认</button>
</div>
</modal-inner>
</template>
<script>
import giteeGistProvider from '../../../services/providers/giteeGistProvider';
import modalTemplate from '../common/modalTemplate';
export default modalTemplate({
data: () => ({
filename: '',
gistId: '',
}),
computedLocalSettings: {
isPublic: 'gistIsPublic',
},
created() {
this.filename = `${this.currentFileName}.md`;
},
methods: {
resolve() {
if (!this.filename) {
this.setError('filename');
} else {
// Return new location
const location = giteeGistProvider.makeLocation(
this.config.token,
this.filename,
this.isPublic,
this.gistId,
);
this.config.resolve(location);
}
},
},
});
</script>

View File

@ -4,7 +4,7 @@
<div class="modal__image"> <div class="modal__image">
<icon-provider provider-id="gitee"></icon-provider> <icon-provider provider-id="gitee"></icon-provider>
</div> </div>
<p>保存 <b>{{currentFileName}}</b> 并与您的 <b>Gitee</b> 仓库保持同步.</p> <p>Save <b>{{currentFileName}}</b> to your <b>Gitee</b> repository and keep it synced.</p>
<form-entry label="仓库URL" error="repoUrl"> <form-entry label="仓库URL" error="repoUrl">
<input slot="field" class="textfield" type="text" v-model.trim="repoUrl" @keydown.enter="resolve()"> <input slot="field" class="textfield" type="text" v-model.trim="repoUrl" @keydown.enter="resolve()">
<div class="form-entry__info"> <div class="form-entry__info">

View File

@ -12,10 +12,9 @@
</div> </div>
</form-entry> </form-entry>
<form-entry label="文件夹路径" info="可选的"> <form-entry label="文件夹路径" info="可选的">
<input slot="field" class="textfield" type="text" placeholder="如imgs/{YYYY}-{MM}-{DD}" v-model.trim="path" @keydown.enter="resolve()"> <input slot="field" class="textfield" type="text" placeholder="如imgs/{YYYY}/{MM}" v-model.trim="path" @keydown.enter="resolve()">
<div class="form-entry__info"> <div class="form-entry__info">
如果不提供默认为 imgs/{YYYY}-{MM}-{DD} <br/> 如果不提供默认为 {YYYY}/{MM}/{DD} 其中{YYYY}为年变量{MM}为月变量{DD}为日变量
变量说明{YYYY}为年变量{MM}为月变量{DD}为日变量{MDNAME}为当前文档名称
</div> </div>
</form-entry> </form-entry>
<form-entry label="分支" info="可选的"> <form-entry label="分支" info="可选的">
@ -64,7 +63,7 @@ export default modalTemplate({
this.config.resolve({ this.config.resolve({
owner, owner,
repo, repo,
path: path || 'imgs/{YYYY}-{MM}-{DD}', path: path || '{YYYY}/{MM}/{DD}',
branch: this.branch || 'master', branch: this.branch || 'master',
}); });
} catch (err) { } catch (err) {

View File

@ -5,22 +5,15 @@
<icon-provider provider-id="gitlab"></icon-provider> <icon-provider provider-id="gitlab"></icon-provider>
</div> </div>
<p>将您的<b>GitLab</b>链接到<b>StackEdit中文版</b></p> <p>将您的<b>GitLab</b>链接到<b>StackEdit中文版</b></p>
<template v-if="!useServerConf">
<form-entry label="GitLab URL" error="serverUrl"> <form-entry label="GitLab URL" error="serverUrl">
<input v-if="config.forceServerUrl" slot="field" class="textfield" type="text" disabled="disabled" v-model="config.forceServerUrl"> <input v-if="config.forceServerUrl" slot="field" class="textfield" type="text" disabled="disabled" v-model="config.forceServerUrl">
<input v-else slot="field" class="textfield" type="text" v-model.trim="serverUrl" @keydown.enter="resolve()"> <input v-else slot="field" class="textfield" type="text" v-model.trim="serverUrl" @keydown.enter="resolve()">
<div class="form-entry__info"> <div class="form-entry__info">
<b>例如:</b> https://gitlab.example.com/ <b>例如:</b> https://gitlab.example.com/
<span v-if="httpAppUrl">
非https的URL请跳转到 <a :href="httpAppUrl" target="_blank">HTTP链接</a> 添加Gitlab
</span>
</div> </div>
</form-entry> </form-entry>
<form-entry label="Application ID" error="applicationId"> <form-entry label="Application ID" error="applicationId">
<input slot="field" class="textfield" type="text" v-model.trim="applicationId" @keydown.enter="resolve()"> <input slot="field" class="textfield" type="text" v-model.trim="applicationId" @keydown.enter="resolve()">
</form-entry>
<form-entry label="Application Secret" error="applicationSecret">
<input slot="field" class="textfield" type="text" v-model.trim="applicationSecret" @keydown.enter="resolve()">
<div class="form-entry__info"> <div class="form-entry__info">
您必须使用重定向url <b>{{redirectUrl}}</b>配置OAuth2应用程序 您必须使用重定向url <b>{{redirectUrl}}</b>配置OAuth2应用程序
</div> </div>
@ -28,7 +21,6 @@
<a href="https://docs.gitlab.com/ee/integration/oauth_provider.html" target="_blank">更多信息</a> <a href="https://docs.gitlab.com/ee/integration/oauth_provider.html" target="_blank">更多信息</a>
</div> </div>
</form-entry> </form-entry>
</template>
</div> </div>
<div class="modal__button-bar"> <div class="modal__button-bar">
<button class="button" @click="config.reject()">取消</button> <button class="button" @click="config.reject()">取消</button>
@ -40,8 +32,6 @@
<script> <script>
import modalTemplate from '../common/modalTemplate'; import modalTemplate from '../common/modalTemplate';
import constants from '../../../data/constants'; import constants from '../../../data/constants';
import store from '../../../store';
import networkSvc from '../../../services/networkSvc';
export default modalTemplate({ export default modalTemplate({
data: () => ({ data: () => ({
@ -50,31 +40,9 @@ export default modalTemplate({
computedLocalSettings: { computedLocalSettings: {
serverUrl: 'gitlabServerUrl', serverUrl: 'gitlabServerUrl',
applicationId: 'gitlabApplicationId', applicationId: 'gitlabApplicationId',
applicationSecret: 'gitlabApplicationSecret',
},
computed: {
httpAppUrl() {
if (constants.origin.indexOf('https://') === 0 && this.serverUrl.indexOf('http://') === 0) {
return `${constants.origin.replace('https://', 'http://')}/app`;
}
return null;
},
// 使
useServerConf() {
const confClientId = store.getters['data/serverConf'].gitlabClientId;
const confServerUrl = store.getters['data/serverConf'].gitlabUrl;
return !!confClientId && !!confServerUrl;
},
},
mounted() {
networkSvc.getServerConf();
}, },
methods: { methods: {
resolve() { resolve() {
if (this.useServerConf) {
this.config.resolve({});
return;
}
const serverUrl = this.config.forceServerUrl || this.serverUrl; const serverUrl = this.config.forceServerUrl || this.serverUrl;
if (!serverUrl) { if (!serverUrl) {
this.setError('serverUrl'); this.setError('serverUrl');
@ -82,18 +50,14 @@ export default modalTemplate({
if (!this.applicationId) { if (!this.applicationId) {
this.setError('applicationId'); this.setError('applicationId');
} }
if (!this.applicationSecret) { if (serverUrl && this.applicationId) {
this.setError('applicationSecret'); const parsedUrl = serverUrl.match(/^(https:\/\/[^/]+)/);
}
if (serverUrl && this.applicationId && this.applicationSecret) {
const parsedUrl = serverUrl.match(/^(http[s]?:\/\/[^/]+)/);
if (!parsedUrl) { if (!parsedUrl) {
this.setError('serverUrl'); this.setError('serverUrl');
} else { } else {
this.config.resolve({ this.config.resolve({
serverUrl: parsedUrl[1], serverUrl: parsedUrl[1],
applicationId: this.applicationId, applicationId: this.applicationId,
applicationSecret: this.applicationSecret,
}); });
} }
} }

View File

@ -22,6 +22,6 @@ export default {
'badgeCreations', 'badgeCreations',
'serverConf', 'serverConf',
], ],
textMaxLength: 10000000, textMaxLength: 250000,
defaultName: 'Untitled', defaultName: 'Untitled',
}; };

View File

@ -24,7 +24,6 @@ export default () => ({
giteePublishTemplate: 'jekyllSite', giteePublishTemplate: 'jekyllSite',
gitlabServerUrl: '', gitlabServerUrl: '',
gitlabApplicationId: '', gitlabApplicationId: '',
gitlabApplicationSecret: '',
gitlabProjectUrl: '', gitlabProjectUrl: '',
gitlabWorkspaceProjectUrl: '', gitlabWorkspaceProjectUrl: '',
gitlabPublishTemplate: 'plainText', gitlabPublishTemplate: 'plainText',

View File

@ -17,34 +17,6 @@ editor:
monospacedFontOnly: false monospacedFontOnly: false
# 是否显示右上角图标 # 是否显示右上角图标
showInPageButtons: true showInPageButtons: true
# 头部的按钮是否显示独立设置
headButtons:
# 加粗
bold: true
# 斜体
italic: true
# 标题
heading: true
# 删除线
strikethrough: true
# 无序列表
ulist: true
# 有序列表
olist: true
# 可选列表
clist: true
# 块引用
quote: true
# 代码
code: true
# 表格
table: true
# 链接
link: true
# 图片
image: true
# ChatGPT
chatgpt: true
# Keyboard shortcuts # Keyboard shortcuts
# See https://craig.is/killing/mice # See https://craig.is/killing/mice
@ -59,7 +31,6 @@ shortcuts:
mod+shift+h: heading mod+shift+h: heading
mod+shift+r: hr mod+shift+r: hr
mod+shift+g: image mod+shift+g: image
mod+shift+p: chatgpt
mod+shift+i: italic mod+shift+i: italic
mod+shift+l: link mod+shift+l: link
mod+shift+o: olist mod+shift+o: olist
@ -67,9 +38,6 @@ shortcuts:
mod+shift+s: strikethrough mod+shift+s: strikethrough
mod+shift+t: table mod+shift+t: table
mod+shift+u: ulist mod+shift+u: ulist
mod+shift+f: inlineformula
# 切换编辑与预览模式
mod+shift+e: toggleeditor
'= = > space': '= = > space':
method: expand method: expand
params: params:
@ -113,16 +81,16 @@ turndown:
# GitHub/GitLab/Gitee/Gitea commit messages # GitHub/GitLab/Gitee/Gitea commit messages
git: git:
createFileMessage: '{{path}} created from https://md.jonylee.top/' createFileMessage: '{{path}} created from https://stackedit.cn/'
updateFileMessage: '{{path}} updated from https://md.jonylee.top/' updateFileMessage: '{{path}} updated from https://stackedit.cn/'
deleteFileMessage: '{{path}} deleted from https://md.jonylee.top/' deleteFileMessage: '{{path}} deleted from https://stackedit.cn/'
# Default content for new files # Default content for new files
newFileContent: | newFileContent: |
> Written with [Markdown编辑器-StackEdit中文版](https://md.jonylee.top/). > Written with [StackEdit中文版](https://stackedit.cn/).
# Default properties for new files # Default properties for new files
newFileProperties: | newFileProperties: |

View File

@ -158,19 +158,7 @@ export default [
new Feature( new Feature(
'sponsor', 'sponsor',
'赞助', '赞助',
'使用 Gitee 登录并赞助 StackEdit 以解锁 PDF 和 Pandoc 导出。(暂不支持赞助)', '使用 Google 登录并赞助 StackEdit 以解锁 PDF 和 Pandoc 导出。(暂不支持赞助)',
),
],
),
new Feature(
'githubSignIn',
'登录',
'使用 Gitee 登录,同步您的主文档空间并解锁功能。',
[
new Feature(
'githubSyncMainWorkspace',
'主文档空间已同步',
'使用 GitHub 登录以将您的主文档空间与您的默认空间stackedit-app-data仓库数据同步。',
), ),
], ],
), ),
@ -328,11 +316,6 @@ export default [
'GitHub保存', 'GitHub保存',
'使用“同步”菜单将文件保存在GitHub仓库中。', '使用“同步”菜单将文件保存在GitHub仓库中。',
), ),
new Feature(
'saveOnGist',
'GitHubGist保存',
'使用“同步”菜单将文件保存在GitHubGist中。',
),
new Feature( new Feature(
'openFromGitee', 'openFromGitee',
'Gitee阅读器', 'Gitee阅读器',
@ -344,9 +327,9 @@ export default [
'使用“同步”菜单将文件保存在Gitee仓库中。', '使用“同步”菜单将文件保存在Gitee仓库中。',
), ),
new Feature( new Feature(
'saveOnGiteeGist', 'saveOnGist',
'GiteeGist保存', 'Gist保存',
'使用“同步”菜单将文件保存在GiteeGist中。', '使用“同步”菜单将文件保存在GIST中。',
), ),
new Feature( new Feature(
'openFromGitlab', 'openFromGitlab',
@ -422,19 +405,14 @@ export default [
), ),
new Feature( new Feature(
'publishToGist', 'publishToGist',
'GitHubGist发布', 'Gist发布',
'使用“发布”菜单将文件发布到GitHubGist。', '使用“发布”菜单将文件发布到GIST。',
), ),
new Feature( new Feature(
'publishToGitee', 'publishToGitee',
'Gitee发布', 'Gitee发布',
'使用“发布”菜单将文件发布到Gitee仓库。', '使用“发布”菜单将文件发布到Gitee仓库。',
), ),
new Feature(
'publishToGiteeGist',
'GiteeGist发布',
'使用“发布”菜单将文件发布到GiteeGist。',
),
new Feature( new Feature(
'publishToGitlab', 'publishToGitlab',
'GitLab发布', 'GitLab发布',

View File

@ -15,7 +15,7 @@ export default [{
method: 'strikethrough', method: 'strikethrough',
title: '删除线', title: '删除线',
icon: 'format-strikethrough', icon: 'format-strikethrough',
// }, { }, {
}, { }, {
method: 'ulist', method: 'ulist',
title: '无序列表', title: '无序列表',
@ -28,7 +28,7 @@ export default [{
method: 'clist', method: 'clist',
title: '可选列表', title: '可选列表',
icon: 'format-list-checks', icon: 'format-list-checks',
// }, { }, {
}, { }, {
method: 'quote', method: 'quote',
title: '块引用', title: '块引用',
@ -49,8 +49,4 @@ export default [{
method: 'image', method: 'image',
title: '图片', title: '图片',
icon: 'file-image', icon: 'file-image',
}, {
method: 'chatgpt',
title: 'ChatGPT',
icon: 'chat-gpt',
}]; }];

View File

@ -1,7 +1,6 @@
const simpleModal = (contentHtml, rejectText, resolveText, resolveArray) => ({ const simpleModal = (contentHtml, rejectText, resolveText) => ({
contentHtml: typeof contentHtml === 'function' ? contentHtml : () => contentHtml, contentHtml: typeof contentHtml === 'function' ? contentHtml : () => contentHtml,
rejectText, rejectText,
resolveArray,
resolveText, resolveText,
}); });
@ -61,40 +60,17 @@ export default {
'取消', '取消',
'确认清理', '确认清理',
), ),
shareHtml: simpleModal(
config => `<p>给文档 "${config.name}" 创建了分享链接如下:<br/><a href="${config.url}" target="_blank">${config.url}</a><br/>关闭该窗口后可以到发布中查看分享链接。</p>`,
'关闭窗口',
),
shareHtmlPre: simpleModal(
config => `<p>将给文档 "${config.name}" 创建分享链接创建后将会把文档公开发布到默认空间账号的Gist中。您确定吗</p>`,
'取消',
'确认分享',
),
signInForComment: simpleModal( signInForComment: simpleModal(
`<p>您必须使用 Gitee或GitHub 登录默认文档空间后才能开始评论。</p> `<p>您必须使用 Google 登录才能开始评论。</p>
<div class="modal__info"><b>注意:</b> </div>`, <div class="modal__info"><b>注意:</b> </div>`,
'取消', '取消',
'', '确认登录',
[{
text: 'Gitee登录',
value: 'gitee',
}, {
text: 'GitHub登录',
value: 'github',
}],
), ),
signInForSponsorship: simpleModal( signInForSponsorship: simpleModal(
`<p>您必须使用 Gitee或GitHub 登录才能赞助。</p> `<p>您必须使用 Google 登录才能赞助。</p>
<div class="modal__info"><b>注意:</b> </div>`, <div class="modal__info"><b>注意:</b> </div>`,
'取消', '取消',
'', '确认登录',
[{
text: 'Gitee登录',
value: 'gitee',
}, {
text: 'GitHub登录',
value: 'github',
}],
), ),
sponsorOnly: simpleModal( sponsorOnly: simpleModal(
'<p>此功能仅限于赞助商,因为它依赖于服务器资源。</p>', '<p>此功能仅限于赞助商,因为它依赖于服务器资源。</p>',

View File

@ -1,43 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{files.0.name}}</title>
<link rel="stylesheet" href="https://stackedit.cn/style.css" />
<style type="text/css">
.app--dark {
background-color: #444;
}
.app--dark .stackedit__html {
padding-left: 0;
padding-right: 0;
}
.app--dark .stackedit__content {
padding: 1px 20px 20px;
}
{{{files.0.content.themeStyleContent}}}
</style>
</head>
{{#if pdf}}
<body class="stackedit stackedit--pdf">
{{else}}
<body class="stackedit {{{files.0.content.colorThemeClass}}}">
{{/if}}
<div class="stackedit__left">
<div class="stackedit__toc">
{{#tocToHtml files.0.content.toc 2}}{{/tocToHtml}}
</div>
</div>
<div class="stackedit__right">
<div class="stackedit__html">
<div class="stackedit__content {{{files.0.content.themeClass}}}">
{{{files.0.content.html}}}
</div>
</div>
</div>
</body>
</html>

View File

@ -1,36 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{files.0.name}}</title>
<link rel="stylesheet" href="https://stackedit.cn/style.css" />
<style type="text/css">
.app--dark {
background-color: #444;
}
.app--dark .stackedit__html {
padding-left: 0;
padding-right: 0;
}
.app--dark .stackedit__content {
padding: 1px 20px 20px;
}
{{{files.0.content.themeStyleContent}}}
</style>
</head>
{{#if pdf}}
<body class="stackedit stackedit--pdf">
{{else}}
<body class="stackedit {{{files.0.content.colorThemeClass}}}">
{{/if}}
<div class="stackedit__html">
<div class="stackedit__content {{{files.0.content.themeClass}}}">
{{{files.0.content.html}}}
</div>
</div>
</body>
</html>

View File

@ -56,7 +56,6 @@ export default (md) => {
// According to http://pandoc.org/README.html#extension-auto_identifiers // According to http://pandoc.org/README.html#extension-auto_identifiers
let slug = headingContent let slug = headingContent
.replace(/<img[^>]*>/g, '') // Replace image to empty
.replace(/\s/g, '-') // Replace all spaces and newlines with hyphens .replace(/\s/g, '-') // Replace all spaces and newlines with hyphens
.replace(/[\0-,/:-@[-^`{-~]/g, '') // Remove all punctuation, except underscores, hyphens, and periods .replace(/[\0-,/:-@[-^`{-~]/g, '') // Remove all punctuation, except underscores, hyphens, and periods
.toLowerCase(); // Convert all alphabetic characters to lowercase .toLowerCase(); // Convert all alphabetic characters to lowercase
@ -65,9 +64,7 @@ export default (md) => {
let i; let i;
for (i = 0; i < slug.length; i += 1) { for (i = 0; i < slug.length; i += 1) {
const charCode = slug.charCodeAt(i); const charCode = slug.charCodeAt(i);
if ((charCode >= 0x30 && charCode <= 0x39) || // 0-9 if ((charCode >= 0x61 && charCode <= 0x7A) || charCode > 0x7E) {
(charCode >= 0x61 && charCode <= 0x7A) || // a-z
charCode > 0x7E) {
break; break;
} }
} }

View File

@ -23,23 +23,13 @@ function texMath(state, silent) {
) { ) {
return false; return false;
} }
function getIndex(tempStartMathPos) { const endMarkerPos = state.src.indexOf(endMarker, startMathPos);
const tempEndMarkerPos = state.src.indexOf(endMarker, tempStartMathPos);
if (tempEndMarkerPos === -1) {
return tempEndMarkerPos;
}
if (state.src.charCodeAt(tempEndMarkerPos - 1) === 0x5C /* \ */) {
if (state.src.length - 1 > tempEndMarkerPos) {
return getIndex(tempEndMarkerPos + 1);
}
return -1;
}
return tempEndMarkerPos;
}
const endMarkerPos = getIndex(startMathPos);
if (endMarkerPos === -1) { if (endMarkerPos === -1) {
return false; return false;
} }
if (state.src.charCodeAt(endMarkerPos - 1) === 0x5C /* \ */) {
return false;
}
const nextPos = endMarkerPos + endMarker.length; const nextPos = endMarkerPos + endMarker.length;
if (endMarker.length === 1) { if (endMarker.length === 1) {
// Skip if $ is preceded by a space character // Skip if $ is preceded by a space character

View File

@ -1,3 +0,0 @@
<template>
<svg class="icon" width="24px" height="24px" viewBox="140 140 520 520"><defs><linearGradient id="linear" x1="100%" y1="22%" x2="0%" y2="78%"><stop offset="0%" stop-color="rgb(131,211,231)"></stop><stop offset="2%" stop-color="rgb(127,203,229)"></stop><stop offset="25%" stop-color="rgb(86,115,217)"></stop><stop offset="49%" stop-color="rgb(105,80,190)"></stop><stop offset="98%" stop-color="rgb(197,59,119)"></stop><stop offset="100%" stop-color="rgb(197,59,119)"></stop></linearGradient></defs><path id="logo" d="m617.24 354a126.36 126.36 0 0 0 -10.86-103.79 127.8 127.8 0 0 0 -137.65-61.32 126.36 126.36 0 0 0 -95.31-42.49 127.81 127.81 0 0 0 -121.92 88.49 126.4 126.4 0 0 0 -84.5 61.3 127.82 127.82 0 0 0 15.72 149.86 126.36 126.36 0 0 0 10.86 103.79 127.81 127.81 0 0 0 137.65 61.32 126.36 126.36 0 0 0 95.31 42.49 127.81 127.81 0 0 0 121.96-88.54 126.4 126.4 0 0 0 84.5-61.3 127.82 127.82 0 0 0 -15.76-149.81zm-190.66 266.49a94.79 94.79 0 0 1 -60.85-22c.77-.42 2.12-1.16 3-1.7l101-58.34a16.42 16.42 0 0 0 8.3-14.37v-142.39l42.69 24.65a1.52 1.52 0 0 1 .83 1.17v117.92a95.18 95.18 0 0 1 -94.97 95.06zm-204.24-87.23a94.74 94.74 0 0 1 -11.34-63.7c.75.45 2.06 1.25 3 1.79l101 58.34a16.44 16.44 0 0 0 16.59 0l123.31-71.2v49.3a1.53 1.53 0 0 1 -.61 1.31l-102.1 58.95a95.16 95.16 0 0 1 -129.85-34.79zm-26.57-220.49a94.71 94.71 0 0 1 49.48-41.68c0 .87-.05 2.41-.05 3.48v116.68a16.41 16.41 0 0 0 8.29 14.36l123.31 71.19-42.69 24.65a1.53 1.53 0 0 1 -1.44.13l-102.11-59a95.16 95.16 0 0 1 -34.79-129.81zm350.74 81.62-123.31-71.2 42.69-24.64a1.53 1.53 0 0 1 1.44-.13l102.11 58.95a95.08 95.08 0 0 1 -14.69 171.55c0-.88 0-2.42 0-3.49v-116.68a16.4 16.4 0 0 0 -8.24-14.36zm42.49-63.95c-.75-.46-2.06-1.25-3-1.79l-101-58.34a16.46 16.46 0 0 0 -16.59 0l-123.31 71.2v-49.3a1.53 1.53 0 0 1 .61-1.31l102.1-58.9a95.07 95.07 0 0 1 141.19 98.44zm-267.11 87.87-42.7-24.65a1.52 1.52 0 0 1 -.83-1.17v-117.92a95.07 95.07 0 0 1 155.9-73c-.77.42-2.11 1.16-3 1.7l-101 58.34a16.41 16.41 0 0 0 -8.3 14.36zm23.19-50 54.92-31.72 54.92 31.7v63.42l-54.92 31.7-54.92-31.7z" fill="#202123"></path></svg>
</template>

View File

@ -1,3 +0,0 @@
<template>
<svg t="1669462755056" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6069" width="32" height="32"><path d="M704 202.666667a96 96 0 0 1 96 96v554.666666a96 96 0 0 1-96 96H213.333333A96 96 0 0 1 117.333333 853.333333V298.666667A96 96 0 0 1 213.333333 202.666667h490.666667z m0 64H213.333333A32 32 0 0 0 181.333333 298.666667v554.666666a32 32 0 0 0 32 32h490.666667a32 32 0 0 0 32-32V298.666667a32 32 0 0 0-32-32z" fill="#212121" p-id="6070"></path><path d="M277.333333 362.666667m32 0l298.666667 0q32 0 32 32l0 0q0 32-32 32l-298.666667 0q-32 0-32-32l0 0q0-32 32-32Z" fill="#212121" p-id="6071"></path><path d="M277.333333 512m32 0l298.666667 0q32 0 32 32l0 0q0 32-32 32l-298.666667 0q-32 0-32-32l0 0q0-32 32-32Z" fill="#212121" p-id="6072"></path><path d="M277.333333 661.333333m32 0l170.666667 0q32 0 32 32l0 0q0 32-32 32l-170.666667 0q-32 0-32-32l0 0q0-32 32-32Z" fill="#212121" p-id="6073"></path><path d="M320 138.666667h512A32 32 0 0 1 864 170.666667v576a32 32 0 0 0 64 0V170.666667A96 96 0 0 0 832 74.666667H320a32 32 0 0 0 0 64z" fill="#212121" p-id="6074"></path></svg>
</template>

View File

@ -1,3 +0,0 @@
<template>
<svg t="1669464773854" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8666" width="32" height="32"><path d="M621.714286 713.142857l0 109.714286q0 22.820571-16.018286 38.838857t-38.838857 16.018286l-109.714286 0q-22.820571 0-38.838857-16.018286t-16.018286-38.838857l0-109.714286q0-22.820571 16.018286-38.838857t38.838857-16.018286l109.714286 0q22.820571 0 38.838857 16.018286t16.018286 38.838857zM621.714286 420.571429l0 109.714286q0 22.820571-16.018286 38.838857t-38.838857 16.018286l-109.714286 0q-22.820571 0-38.838857-16.018286t-16.018286-38.838857l0-109.714286q0-22.820571 16.018286-38.838857t38.838857-16.018286l109.714286 0q22.820571 0 38.838857 16.018286t16.018286 38.838857zM621.714286 128l0 109.714286q0 22.820571-16.018286 38.838857t-38.838857 16.018286l-109.714286 0q-22.820571 0-38.838857-16.018286t-16.018286-38.838857l0-109.714286q0-22.820571 16.018286-38.838857t38.838857-16.018286l109.714286 0q22.820571 0 38.838857 16.018286t16.018286 38.838857z" p-id="8667"></path></svg>
</template>

View File

@ -15,7 +15,6 @@ export default {
return 'google-drive'; return 'google-drive';
case 'googlePhotos': case 'googlePhotos':
return 'google-photos'; return 'google-photos';
case 'githubAppData':
case 'githubWorkspace': case 'githubWorkspace':
return 'github'; return 'github';
case 'gist': case 'gist':
@ -30,10 +29,7 @@ export default {
return 'couchdb'; return 'couchdb';
case 'giteeAppData': case 'giteeAppData':
case 'giteeWorkspace': case 'giteeWorkspace':
case 'giteegist':
return 'gitee'; return 'gitee';
case 'stackedit':
return 'stackedit';
default: default:
return this.providerId; return this.providerId;
} }

View File

@ -1,3 +0,0 @@
<template>
<svg t="1680140298859" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2766" width="32" height="32"><path d="M769.14815 670.390403c-44.430932 0-84.182284 19.999496-110.768803 51.471278L389.219117 565.736878c6.597255-16.571421 10.228969-34.653241 10.228969-53.594639 0-17.006326-2.940982-33.332153-8.320503-48.497551l270.88143-157.119457c26.511817 29.069059 64.702628 47.312562 107.138112 47.312562 80.055291 0 144.95337-64.899102 144.95337-144.95337 0-80.055291-64.898079-144.954393-144.95337-144.954393s-144.95337 64.899102-144.95337 144.954393c0 15.991206 2.600221 31.386848 7.382131 45.776579L359.655801 412.377048c-26.417673-27.833929-63.756069-45.181015-105.161085-45.181015-80.055291 0-144.954393 64.890916-144.954393 144.946206 0 80.055291 64.898079 144.967696 144.954393 144.967696 39.409568 0 75.128071-15.741519 101.256148-41.24845l274.172383 159.024853c-3.725858 12.8384-5.729491 26.409486-5.729491 40.457434 0 80.0645 64.898079 144.954393 144.95337 144.954393s144.95337-64.889893 144.95337-144.954393C914.101519 735.297692 849.20344 670.390403 769.14815 670.390403z" p-id="2767"></path></svg>
</template>

View File

@ -62,10 +62,6 @@ import SwitchTheme from './SwitchTheme';
import Search from './Search'; import Search from './Search';
import FindReplace from './FindReplace'; import FindReplace from './FindReplace';
import SelectTheme from './SelectTheme'; import SelectTheme from './SelectTheme';
import Copy from './Copy';
import Ellipsis from './Ellipsis';
import Share from './Share';
import ChatGpt from './ChatGpt';
Vue.component('iconProvider', Provider); Vue.component('iconProvider', Provider);
Vue.component('iconFormatBold', FormatBold); Vue.component('iconFormatBold', FormatBold);
@ -130,7 +126,3 @@ Vue.component('iconSwitchTheme', SwitchTheme);
Vue.component('iconSearch', Search); Vue.component('iconSearch', Search);
Vue.component('iconFindReplace', FindReplace); Vue.component('iconFindReplace', FindReplace);
Vue.component('iconSelectTheme', SelectTheme); Vue.component('iconSelectTheme', SelectTheme);
Vue.component('iconCopy', Copy);
Vue.component('iconEllipsis', Ellipsis);
Vue.component('iconShare', Share);
Vue.component('iconChatGpt', ChatGpt);

View File

@ -40,9 +40,7 @@ var defaultsStrings = {
undo: "Undo - Ctrl/Cmd+Z", undo: "Undo - Ctrl/Cmd+Z",
redo: "Redo - Ctrl/Cmd+Y", redo: "Redo - Ctrl/Cmd+Y",
help: "Markdown Editing Help", help: "Markdown Editing Help"
formulaexample: "这里输入Latex表达式",
}; };
// options, if given, can have the following properties: // options, if given, can have the following properties:
@ -122,7 +120,6 @@ function Pagedown(options) {
hooks.addNoop("onPreviewRefresh"); // called with no arguments after the preview has been refreshed hooks.addNoop("onPreviewRefresh"); // called with no arguments after the preview has been refreshed
hooks.addNoop("postBlockquoteCreation"); // called with the user's selection *after* the blockquote was created; should return the actual to-be-inserted text hooks.addNoop("postBlockquoteCreation"); // called with the user's selection *after* the blockquote was created; should return the actual to-be-inserted text
hooks.addFalse("insertImageDialog"); hooks.addFalse("insertImageDialog");
hooks.addFalse("insertChatGptDialog");
/* called with one parameter: a callback to be called with the URL of the image. If the application creates /* called with one parameter: a callback to be called with the URL of the image. If the application creates
* its own image insertion dialog, this hook should return true, and the callback should be called with the chosen * its own image insertion dialog, this hook should return true, and the callback should be called with the chosen
* image url (or null if the user cancelled). If this hook returns false, the default dialog will be used. * image url (or null if the user cancelled). If this hook returns false, the default dialog will be used.
@ -468,7 +465,6 @@ function UIManager(input, commandManager) {
buttons.bold = bindCommand("doBold"); buttons.bold = bindCommand("doBold");
buttons.italic = bindCommand("doItalic"); buttons.italic = bindCommand("doItalic");
buttons.strikethrough = bindCommand("doStrikethrough"); buttons.strikethrough = bindCommand("doStrikethrough");
buttons.inlineformula = bindCommand("doInlinkeFormula");
buttons.imageUploading = bindCommand("doImageUploading"); buttons.imageUploading = bindCommand("doImageUploading");
buttons.link = bindCommand(function (chunk, postProcessing) { buttons.link = bindCommand(function (chunk, postProcessing) {
return this.doLinkOrImage(chunk, postProcessing, false); return this.doLinkOrImage(chunk, postProcessing, false);
@ -478,7 +474,6 @@ function UIManager(input, commandManager) {
buttons.image = bindCommand(function (chunk, postProcessing) { buttons.image = bindCommand(function (chunk, postProcessing) {
return this.doLinkOrImage(chunk, postProcessing, true); return this.doLinkOrImage(chunk, postProcessing, true);
}); });
buttons.chatgpt = bindCommand("doChatGpt");
buttons.olist = bindCommand(function (chunk, postProcessing) { buttons.olist = bindCommand(function (chunk, postProcessing) {
this.doList(chunk, postProcessing, true); this.doList(chunk, postProcessing, true);
}); });
@ -623,49 +618,6 @@ commandProto.doStrikethrough = function (chunk, postProcessing) {
return; return;
}; };
commandProto.doInlinkeFormula = function (chunk, postProcessing) {
// Get rid of whitespace and fixup newlines.
chunk.trimWhitespace();
chunk.selection = chunk.selection.replace(/\n{2,}/g, "\n");
// Look for stars before and after. Is the chunk already marked up?
// note that these regex matches cannot fail
var starsBefore = /(\$*$)/.exec(chunk.before)[0];
var starsAfter = /(^\$*)/.exec(chunk.after)[0];
var prevStars = Math.min(starsBefore.length, starsAfter.length);
var nStars = 2;
// Remove stars if we have to since the button acts as a toggle.
if ((prevStars >= nStars) && (prevStars != 2 || nStars != 1)) {
chunk.before = chunk.before.replace(re("[\$]{" + nStars + "}$", ""), "");
chunk.after = chunk.after.replace(re("^[\$]{" + nStars + "}", ""), "");
} else if (!chunk.selection && starsAfter) {
// It's not really clear why this code is necessary. It just moves
// some arbitrary stuff around.
chunk.after = chunk.after.replace(/^(\$*)/, "");
chunk.before = chunk.before.replace(/(\s?)$/, "");
var whitespace = re.$1;
chunk.before = chunk.before + starsAfter + whitespace;
} else {
// In most cases, if you don't have any selected text and click the button
// you'll get a selected, marked up region with the default text inserted.
if (!chunk.selection && !starsAfter) {
chunk.selection = this.getString("formulaexample");
}
// Add the true markup.
var markup = "$"; // shouldn't the test be = ?
chunk.before = chunk.before + markup;
chunk.after = markup + chunk.after;
}
return;
};
commandProto.doImageUploading = function (chunk, postProcessing) { commandProto.doImageUploading = function (chunk, postProcessing) {
var enteredCallback = function (imgId) { var enteredCallback = function (imgId) {
if (imgId !== null) { if (imgId !== null) {
@ -848,17 +800,6 @@ commandProto.doLinkOrImage = function (chunk, postProcessing, isImage) {
} }
}; };
commandProto.doChatGpt = function (chunk, postProcessing) {
var enteredCallback = function (content) {
if (content !== null) {
chunk.before = `${chunk.before}${content}`;
chunk.selection = '';
}
postProcessing();
};
this.hooks.insertChatGptDialog(enteredCallback);
};
// When making a list, hitting shift-enter will put your cursor on the next line // When making a list, hitting shift-enter will put your cursor on the next line
// at the current indent level. // at the current indent level.
commandProto.doAutoindent = function (chunk) { commandProto.doAutoindent = function (chunk) {

View File

@ -1,42 +0,0 @@
import store from '../store';
export default {
chat({ content }, callback) {
const xhr = new XMLHttpRequest();
const url = 'https://api.openai-proxy.com/v1/chat/completions';
xhr.open('POST', url);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Authorization', `Bearer ${window.my_api_key}`);
xhr.send(JSON.stringify({
model: 'gpt-3.5-turbo',
max_tokens: 3000,
top_p: 0,
temperature: 0.9,
frequency_penalty: 0,
presence_penalty: 0,
messages: [{ role: 'user', content }],
stream: true,
}));
let lastRespLen = 0;
xhr.onprogress = () => {
const responseText = xhr.response.substr(lastRespLen);
lastRespLen = xhr.response.length;
responseText.split('\n\n')
.filter(l => l.length > 0)
.forEach((text) => {
const item = text.substr(6);
if (item === '[DONE]') {
callback({ done: true });
} else {
const data = JSON.parse(item);
callback({ content: data.choices[0].delta.content });
}
});
};
xhr.onerror = () => {
store.dispatch('notification/error', 'ChatGPT接口请求异常');
callback({ error: 'ChatGPT接口请求异常' });
};
return xhr;
},
};

View File

@ -46,10 +46,16 @@ class SectionDesc {
const pathUrlMap = Object.create(null); const pathUrlMap = Object.create(null);
const getCurrAbsolutePath = () => {
const fileId = store.getters['file/current'].id;
const fileSyncData = store.getters['data/syncDataByItemId'][fileId] || { id: '' };
const fileAbsolutePath = `${store.getters['workspace/currentWorkspace'].path || ''}${fileSyncData.id}`;
return fileAbsolutePath.substring(0, fileAbsolutePath.lastIndexOf('/'));
};
const getImgUrl = async (uri) => { const getImgUrl = async (uri) => {
if (uri.indexOf('http://') !== 0 && uri.indexOf('https://') !== 0) { if (uri.indexOf('http://') !== 0 && uri.indexOf('https://') !== 0) {
const currDirNode = store.getters['explorer/selectedNodeFolder']; const absoluteImgPath = utils.getAbsoluteFilePath(getCurrAbsolutePath(), uri);
const absoluteImgPath = utils.getAbsoluteFilePath(currDirNode, uri);
if (pathUrlMap[absoluteImgPath]) { if (pathUrlMap[absoluteImgPath]) {
return pathUrlMap[absoluteImgPath]; return pathUrlMap[absoluteImgPath];
} }
@ -203,28 +209,11 @@ const editorSvc = Object.assign(new Vue(), editorSvcDiscussions, editorSvcUtils,
this.previewElt.appendChild(sectionPreviewElt); this.previewElt.appendChild(sectionPreviewElt);
} }
extensionSvc.sectionPreview(sectionPreviewElt, this.options, true); extensionSvc.sectionPreview(sectionPreviewElt, this.options, true);
const imgs = Array.prototype.slice.call(sectionPreviewElt.getElementsByTagName('img')).map((imgElt) => {
if (imgElt.src.indexOf(constants.origin) >= 0) {
const uri = decodeURIComponent(imgElt.attributes.src.nodeValue);
imgElt.removeAttribute('src');
return { imgElt, uri };
}
return { imgElt };
});
loadingImages = [ loadingImages = [
...loadingImages, ...loadingImages,
...imgs, ...Array.prototype.slice.call(sectionPreviewElt.getElementsByTagName('img')),
]; ];
Array.prototype.slice.call(sectionPreviewElt.getElementsByTagName('a')).forEach((aElt) => {
const url = aElt.attributes && aElt.attributes.href && aElt.attributes.href.nodeValue;
if (!url || url.indexOf('http://') >= 0 || url.indexOf('https://') >= 0 || url.indexOf('#') >= 0) {
return;
}
aElt.href = 'javascript:void(0);'; // eslint-disable-line no-script-url
aElt.setAttribute('onclick', `window.viewFileByPath('${utils.decodeUrlPath(url)}')`);
});
// Create TOC section element // Create TOC section element
sectionTocElt = document.createElement('div'); sectionTocElt = document.createElement('div');
sectionTocElt.className = 'cl-toc-section'; sectionTocElt.className = 'cl-toc-section';
@ -233,21 +222,6 @@ const editorSvc = Object.assign(new Vue(), editorSvcDiscussions, editorSvcUtils,
const clonedElt = headingElt.cloneNode(true); const clonedElt = headingElt.cloneNode(true);
clonedElt.removeAttribute('id'); clonedElt.removeAttribute('id');
sectionTocElt.appendChild(clonedElt); sectionTocElt.appendChild(clonedElt);
// 创建一个新的 <span> 元素
const contentElt = document.createElement('span');
contentElt.className = 'content';
// 将原始内容移动到新的 <span> 元素中
while (headingElt.firstChild) {
contentElt.appendChild(headingElt.firstChild);
}
const prefixElt = document.createElement('span');
prefixElt.className = 'prefix';
headingElt.insertBefore(prefixElt, headingElt.firstChild);
// 将新的 <span> 元素替换原始元素
headingElt.appendChild(contentElt);
const suffixElt = document.createElement('span');
suffixElt.className = 'suffix';
headingElt.appendChild(suffixElt);
} }
if (insertBeforeTocElt) { if (insertBeforeTocElt) {
this.tocElt.insertBefore(sectionTocElt, insertBeforeTocElt); this.tocElt.insertBefore(sectionTocElt, insertBeforeTocElt);
@ -275,22 +249,22 @@ const editorSvc = Object.assign(new Vue(), editorSvcDiscussions, editorSvcUtils,
this.makeTextToPreviewDiffs(); this.makeTextToPreviewDiffs();
// Wait for images to load // Wait for images to load
const loadedPromises = loadingImages.map(it => new Promise((resolve, reject) => { const loadedPromises = loadingImages.map(imgElt => new Promise((resolve, reject) => {
if (!it.imgElt.src && it.uri) { if (!imgElt.src) {
getImgUrl(it.uri).then((newUrl) => {
it.imgElt.src = newUrl;
resolve(); resolve();
}, () => reject(new Error('加载当前空间图片出错')));
return; return;
} }
if (!it.imgElt.src) { if (imgElt.src.indexOf(constants.origin) >= 0) {
getImgUrl(imgElt.src.replace(constants.origin, '')).then((newUrl) => {
imgElt.src = newUrl;
resolve(); resolve();
}, () => reject(new Error('加载本地空间图片出错')));
return; return;
} }
const img = new window.Image(); const img = new window.Image();
img.onload = resolve; img.onload = resolve;
img.onerror = resolve; img.onerror = resolve;
img.src = it.imgElt.src; img.src = imgElt.src;
})); }));
await Promise.all(loadedPromises); await Promise.all(loadedPromises);
@ -453,13 +427,6 @@ const editorSvc = Object.assign(new Vue(), editorSvcDiscussions, editorSvcUtils,
}); });
return true; return true;
}); });
this.pagedownEditor.hooks.set('insertChatGptDialog', (callback) => {
store.dispatch('modal/open', {
type: 'chatGpt',
callback,
});
return true;
});
this.pagedownEditor.hooks.set('insertImageUploading', (callback) => { this.pagedownEditor.hooks.set('insertImageUploading', (callback) => {
callback(store.getters['img/currImgId']); callback(store.getters['img/currImgId']);
return true; return true;
@ -575,8 +542,7 @@ const editorSvc = Object.assign(new Vue(), editorSvcDiscussions, editorSvcUtils,
} }
imgEltsToCache.push(imgElt); imgEltsToCache.push(imgElt);
if (imgElt.src.indexOf(origin) >= 0) { if (imgElt.src.indexOf(origin) >= 0) {
imgElt.removeAttribute('src'); loadImgs.push(imgElt);
loadImgs.push({ imgElt, uri: decodeURIComponent(uri) });
} }
} }
const imgTokenWrapper = document.createElement('span'); const imgTokenWrapper = document.createElement('span');
@ -588,11 +554,12 @@ const editorSvc = Object.assign(new Vue(), editorSvcDiscussions, editorSvcUtils,
}); });
if (loadImgs.length) { if (loadImgs.length) {
// Wait for images to load // Wait for images to load
const loadWorkspaceImg = loadImgs.map(it => new Promise((resolve, reject) => { const loadWorkspaceImg = loadImgs.map(imgElt => new Promise((resolve, reject) => {
getImgUrl(it.uri).then((newUrl) => { const uri = imgElt.src.replace(origin, '');
it.imgElt.src = newUrl; getImgUrl(uri).then((newUrl) => {
imgElt.src = newUrl;
resolve(); resolve();
}, () => reject(new Error(`加载当前空间图片出错,uri:${it.uri}`))); }, () => reject(new Error(`加载本地空间图片出错,uri:${uri}`)));
})); }));
Promise.all(loadWorkspaceImg).then(); Promise.all(loadWorkspaceImg).then();
} }

View File

@ -1,4 +1,3 @@
import md5 from 'js-md5';
import FileSaver from 'file-saver'; import FileSaver from 'file-saver';
import TemplateWorker from 'worker-loader!./templateWorker.js'; // eslint-disable-line import TemplateWorker from 'worker-loader!./templateWorker.js'; // eslint-disable-line
import localDbSvc from './localDbSvc'; import localDbSvc from './localDbSvc';
@ -35,24 +34,6 @@ function groupHeadings(headings, level = 1) {
return result; return result;
} }
const getImgBase64 = async (uri) => {
if (uri.indexOf('http://') !== 0 && uri.indexOf('https://') !== 0) {
const currDirNode = store.getters['explorer/selectedNodeFolder'];
const absoluteImgPath = utils.getAbsoluteFilePath(currDirNode, uri);
const md5Id = md5(absoluteImgPath);
const imgItem = await localDbSvc.getImgItem(md5Id);
if (imgItem) {
const potIdx = uri.lastIndexOf('.');
const suffix = potIdx > -1 ? uri.substring(potIdx + 1) : 'png';
const mime = `image/${suffix}`;
return `data:${mime};base64,${imgItem.content}`;
}
return '';
}
return uri;
};
const containerElt = document.createElement('div'); const containerElt = document.createElement('div');
containerElt.className = 'hidden-rendering-container'; containerElt.className = 'hidden-rendering-container';
document.body.appendChild(containerElt); document.body.appendChild(containerElt);
@ -73,13 +54,6 @@ export default {
const parsingCtx = markdownConversionSvc.parseSections(converter, content.text); const parsingCtx = markdownConversionSvc.parseSections(converter, content.text);
const conversionCtx = markdownConversionSvc.convert(parsingCtx); const conversionCtx = markdownConversionSvc.convert(parsingCtx);
const html = conversionCtx.htmlSectionList.map(htmlSanitizer.sanitizeHtml).join(''); const html = conversionCtx.htmlSectionList.map(htmlSanitizer.sanitizeHtml).join('');
const colorThemeClass = `app--${store.getters['data/computedSettings'].colorTheme}`;
const themeClass = `preview-theme--${store.state.theme.currPreviewTheme}`;
let themeStyleContent = '';
const themeStyleEle = document.getElementById(`preview-theme-${store.state.theme.currPreviewTheme}`);
if (themeStyleEle) {
themeStyleContent = themeStyleEle.innerText;
}
containerElt.innerHTML = html; containerElt.innerHTML = html;
extensionSvc.sectionPreview(containerElt, options); extensionSvc.sectionPreview(containerElt, options);
@ -91,48 +65,8 @@ export default {
wrapperElt.parentNode.removeChild(wrapperElt); wrapperElt.parentNode.removeChild(wrapperElt);
}); });
// 替换相对路径图片为blob图片
const imgs = Array.prototype.slice.call(containerElt.getElementsByTagName('img')).map((imgElt) => {
let uri = imgElt.attributes && imgElt.attributes.src && imgElt.attributes.src.nodeValue;
if (uri && uri.indexOf('http://') !== 0 && uri.indexOf('https://') !== 0) {
uri = decodeURIComponent(uri);
imgElt.removeAttribute('src');
return { imgElt, uri };
}
return { imgElt };
});
const loadedPromises = imgs.map(it => new Promise((resolve, reject) => {
if (!it.imgElt.src && it.uri) {
getImgBase64(it.uri).then((newUrl) => {
it.imgElt.src = newUrl;
resolve();
}, () => reject(new Error('加载当前空间图片出错')));
return;
}
resolve();
}));
await Promise.all(loadedPromises);
// Make TOC // Make TOC
const allHeaders = containerElt.querySelectorAll('h1,h2,h3,h4,h5,h6'); const headings = containerElt.querySelectorAll('h1,h2,h3,h4,h5,h6').cl_map(headingElt => ({
Array.prototype.slice.call(allHeaders).forEach((headingElt) => {
// 创建一个新的 <span> 元素
const contentElt = document.createElement('span');
contentElt.className = 'content';
// 将原始内容移动到新的 <span> 元素中
while (headingElt.firstChild) {
contentElt.appendChild(headingElt.firstChild);
}
const prefixElt = document.createElement('span');
prefixElt.className = 'prefix';
headingElt.insertBefore(prefixElt, headingElt.firstChild);
// 将新的 <span> 元素替换原始元素
headingElt.appendChild(contentElt);
const suffixElt = document.createElement('span');
suffixElt.className = 'suffix';
headingElt.appendChild(suffixElt);
});
const headings = allHeaders.cl_map(headingElt => ({
title: headingElt.textContent, title: headingElt.textContent,
anchor: headingElt.id, anchor: headingElt.id,
level: parseInt(headingElt.tagName.slice(1), 10), level: parseInt(headingElt.tagName.slice(1), 10),
@ -149,9 +83,6 @@ export default {
yamlProperties: content.properties, yamlProperties: content.properties,
html: containerElt.innerHTML, html: containerElt.innerHTML,
toc, toc,
colorThemeClass,
themeClass,
themeStyleContent,
}, },
}], }],
}; };

View File

@ -7,15 +7,22 @@ import giteaHelper from '../services/providers/helpers/giteaHelper';
import githubHelper from '../services/providers/helpers/githubHelper'; import githubHelper from '../services/providers/helpers/githubHelper';
import customHelper from '../services/providers/helpers/customHelper'; import customHelper from '../services/providers/helpers/customHelper';
const getImagePath = (confPath, imgType) => { function getCurrAbsolutePath() {
const fileId = store.getters['file/current'].id;
const fileSyncData = store.getters['data/syncDataByItemId'][fileId] || { id: '' };
const fileAbsolutePath = `${store.getters['workspace/currentWorkspace'].path || ''}${fileSyncData.id}`;
return fileAbsolutePath.substring(0, fileAbsolutePath.lastIndexOf('/'));
}
function getImagePath(confPath, imgType) {
const time = new Date(); const time = new Date();
const date = time.getDate(); const date = time.getDate();
const month = time.getMonth() + 1; const month = time.getMonth() + 1;
const year = time.getFullYear(); const year = time.getFullYear();
const path = confPath.replace('{YYYY}', year).replace('{MM}', `0${month}`.slice(-2)) const path = confPath.replace('{YYYY}', year)
.replace('{DD}', `0${date}`.slice(-2)).replace('{MDNAME}', store.getters['file/current'].name); .replace('{MM}', `0${month}`.slice(-2)).replace('{DD}', `0${date}`.slice(-2));
return `${path}${path.endsWith('/') ? '' : '/'}${utils.uid()}.${imgType.split('/')[1]}`; return `${path}${path.endsWith('/') ? '' : '/'}${utils.uid()}.${imgType.split('/')[1]}`;
}; }
export default { export default {
// 上传图片 返回图片链接 // 上传图片 返回图片链接
@ -28,21 +35,16 @@ export default {
} }
// 判断是否文档空间路径 // 判断是否文档空间路径
if (currStorage.type === 'workspace') { if (currStorage.type === 'workspace') {
// 如果不是git仓库 则提示不支持
if (!store.getters['workspace/currentWorkspaceIsGit']) {
return { error: '暂无已选择的图床!' };
}
const path = getImagePath(currStorage.sub, imgFile.type); const path = getImagePath(currStorage.sub, imgFile.type);
// 保存到indexeddb // 保存到indexeddb
const base64 = await utils.encodeFiletoBase64(imgFile); const base64 = await utils.encodeFiletoBase64(imgFile);
const currDirNode = store.getters['explorer/selectedNodeFolder']; const absolutePath = utils.getAbsoluteFilePath(getCurrAbsolutePath(), path);
const absolutePath = utils.getAbsoluteFilePath(currDirNode, path);
await localDbSvc.saveImg({ await localDbSvc.saveImg({
id: md5(absolutePath), id: md5(absolutePath),
path: absolutePath, path: absolutePath,
content: base64, content: base64,
}); });
return { url: path.replaceAll(' ', '%20') }; return { url: path };
} }
if (!currStorage.provider) { if (!currStorage.provider) {
return { error: '暂无已选择的图床!' }; return { error: '暂无已选择的图床!' };

View File

@ -44,7 +44,7 @@ const noSpellcheckTokens = Object.create(null);
[ [
'code', 'code',
'pre', 'pre',
'pre gfm cn-code', 'pre gfm',
'math block', 'math block',
'math inline', 'math inline',
'math expr block', 'math expr block',

View File

@ -69,7 +69,7 @@ export default {
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
script.onload = resolve; script.onload = resolve;
script.onerror = reject; script.onerror = reject;
script.src = `https://res.wx.qq.com/open/js/jweixin-1.2.0.js?${Date.now()}`; script.src = `https://www.gstatic.cn/charts/loader.js?${Date.now()}`;
try { try {
document.head.appendChild(script); // This can fail with bad network document.head.appendChild(script); // This can fail with bad network
timeout = setTimeout(reject, networkTimeout); timeout = setTimeout(reject, networkTimeout);

View File

@ -3,8 +3,8 @@ import store from '../../store';
import editorSvc from '../../services/editorSvc'; import editorSvc from '../../services/editorSvc';
import syncSvc from '../../services/syncSvc'; import syncSvc from '../../services/syncSvc';
// Skip shortcuts if modal is open // Skip shortcuts if modal is open or editor is hidden
Mousetrap.prototype.stopCallback = () => store.getters['modal/config']; Mousetrap.prototype.stopCallback = () => store.getters['modal/config'] || !store.getters['content/isCurrentEditable'];
const pagedownHandler = name => () => { const pagedownHandler = name => () => {
editorSvc.pagedownEditor.uiManager.doClick(name); editorSvc.pagedownEditor.uiManager.doClick(name);
@ -20,14 +20,6 @@ const findReplaceOpener = type => () => {
return true; return true;
}; };
const toggleEditor = () => () => {
store.dispatch('data/toggleEditor', !store.getters['data/layoutSettings'].showEditor);
return true;
};
// 非编辑模式下支持的快捷键
const noEditableShortcutMethods = ['toggleeditor'];
const methods = { const methods = {
bold: pagedownHandler('bold'), bold: pagedownHandler('bold'),
italic: pagedownHandler('italic'), italic: pagedownHandler('italic'),
@ -36,15 +28,11 @@ const methods = {
quote: pagedownHandler('quote'), quote: pagedownHandler('quote'),
code: pagedownHandler('code'), code: pagedownHandler('code'),
image: pagedownHandler('image'), image: pagedownHandler('image'),
chatgpt: pagedownHandler('chatgpt'),
olist: pagedownHandler('olist'), olist: pagedownHandler('olist'),
ulist: pagedownHandler('ulist'), ulist: pagedownHandler('ulist'),
clist: pagedownHandler('clist'), clist: pagedownHandler('clist'),
heading: pagedownHandler('heading'), heading: pagedownHandler('heading'),
inline: pagedownHandler('heading'),
hr: pagedownHandler('hr'), hr: pagedownHandler('hr'),
inlineformula: pagedownHandler('inlineformula'),
toggleeditor: toggleEditor(),
sync() { sync() {
if (syncSvc.isSyncPossible()) { if (syncSvc.isSyncPossible()) {
syncSvc.requestSync(); syncSvc.requestSync();
@ -76,11 +64,8 @@ const methods = {
}; };
store.watch( store.watch(
() => ({ () => store.getters['data/computedSettings'],
computedSettings: store.getters['data/computedSettings'], (computedSettings) => {
isCurrentEditable: store.getters['content/isCurrentEditable'],
}),
({ computedSettings, isCurrentEditable }) => {
Mousetrap.reset(); Mousetrap.reset();
Object.entries(computedSettings.shortcuts).forEach(([key, shortcut]) => { Object.entries(computedSettings.shortcuts).forEach(([key, shortcut]) => {
@ -92,18 +77,14 @@ store.watch(
} }
if (Object.prototype.hasOwnProperty.call(methods, method)) { if (Object.prototype.hasOwnProperty.call(methods, method)) {
try { try {
// editor is editable or 一些非编辑模式下支持的快捷键
if (isCurrentEditable || noEditableShortcutMethods.indexOf(method) !== -1) {
Mousetrap.bind(`${key}`, () => !methods[method].apply(null, params)); Mousetrap.bind(`${key}`, () => !methods[method].apply(null, params));
}
} catch (e) { } catch (e) {
// Ignore // Ignore
} }
} }
} }
}); });
}, }, {
{
immediate: true, immediate: true,
}, },
); );

View File

@ -36,7 +36,7 @@ export default new Provider({
async uploadContent(token, content, syncLocation) { async uploadContent(token, content, syncLocation) {
const updatedSyncLocation = { const updatedSyncLocation = {
...syncLocation, ...syncLocation,
projectId: await giteaHelper.getProjectId(token, syncLocation), projectId: await giteaHelper.getProjectId(syncLocation),
}; };
if (!savedSha[updatedSyncLocation.id]) { if (!savedSha[updatedSyncLocation.id]) {
try { try {
@ -59,7 +59,7 @@ export default new Provider({
async publish(token, html, metadata, publishLocation, commitMessage) { async publish(token, html, metadata, publishLocation, commitMessage) {
const updatedPublishLocation = { const updatedPublishLocation = {
...publishLocation, ...publishLocation,
projectId: await giteaHelper.getProjectId(token, publishLocation), projectId: await giteaHelper.getProjectId(publishLocation),
}; };
try { try {
// Get the last sha // Get the last sha
@ -81,7 +81,7 @@ export default new Provider({
async openFile(token, syncLocation) { async openFile(token, syncLocation) {
const updatedSyncLocation = { const updatedSyncLocation = {
...syncLocation, ...syncLocation,
projectId: await giteaHelper.getProjectId(token, syncLocation), projectId: await giteaHelper.getProjectId(syncLocation),
}; };
// Check if the file exists and open it // Check if the file exists and open it

View File

@ -320,12 +320,4 @@ export default new Provider({
}); });
return Provider.parseContent(data, contentId); return Provider.parseContent(data, contentId);
}, },
getFilePathUrl(path) {
const token = this.getToken();
if (!token) {
return null;
}
const workspace = store.getters['workspace/currentWorkspace'];
return `${token.serverUrl}/${workspace.owner}/${workspace.repo}/src/branch/${workspace.branch}${path}`;
},
}); });

View File

@ -282,11 +282,4 @@ export default new Provider({
}); });
return Provider.parseContent(data, contentId); return Provider.parseContent(data, contentId);
}, },
getFilePathUrl(path) {
const token = this.getToken();
if (!token) {
return null;
}
return `https://gitee.com/${token.name}/${appDataRepo}/blob/${appDataBranch}${path}`;
},
}); });

View File

@ -1,95 +0,0 @@
import store from '../../store';
import giteeHelper from './helpers/giteeHelper';
import Provider from './common/Provider';
import utils from '../utils';
import userSvc from '../userSvc';
export default new Provider({
id: 'giteegist',
name: 'GiteeGist',
getToken({ sub }) {
return store.getters['data/giteeTokensBySub'][sub];
},
getLocationUrl({ gistId }) {
return `https://gitee.com/caojiezi2003/codes/${gistId}`;
},
getLocationDescription({ filename }) {
return filename;
},
async downloadContent(token, syncLocation) {
const content = await giteeHelper.downloadGist({
...syncLocation,
token,
});
return Provider.parseContent(content, `${syncLocation.fileId}/content`);
},
async uploadContent(token, content, syncLocation) {
const file = store.state.file.itemsById[syncLocation.fileId];
const description = utils.sanitizeName(file && file.name);
const gist = await giteeHelper.uploadGist({
...syncLocation,
token,
description,
content: Provider.serializeContent(content),
});
return {
...syncLocation,
gistId: gist.id,
};
},
async publish(token, html, metadata, publishLocation) {
const gist = await giteeHelper.uploadGist({
...publishLocation,
token,
description: metadata.title,
content: html,
});
return {
...publishLocation,
gistId: gist.id,
};
},
makeLocation(token, filename, isPublic, gistId) {
return {
providerId: this.id,
sub: token.sub,
filename,
isPublic,
gistId,
};
},
async listFileRevisions({ token, syncLocation }) {
const entries = await giteeHelper.getGistCommits({
...syncLocation,
token,
});
return entries.map((entry) => {
const sub = `${giteeHelper.subPrefix}:${entry.user.id}`;
userSvc.addUserInfo({ id: sub, name: entry.user.login, imageUrl: entry.user.avatar_url });
return {
sub,
id: entry.version,
message: entry.commit && entry.commit.message,
created: new Date(entry.committed_at).getTime(),
};
});
},
async loadFileRevision() {
// Revision are already loaded
return false;
},
// async getFileRevisionContent({
// token,
// contentId,
// syncLocation,
// revisionId,
// }) {
// const data = await giteeHelper.downloadGistRevision({
// ...syncLocation,
// token,
// sha: revisionId,
// });
// return Provider.parseContent(data, contentId);
// },
});

View File

@ -308,8 +308,4 @@ export default new Provider({
}); });
return Provider.parseContent(data, contentId); return Provider.parseContent(data, contentId);
}, },
getFilePathUrl(path) {
const workspace = store.getters['workspace/currentWorkspace'];
return `https://gitee.com/${workspace.owner}/${workspace.repo}/blob/${workspace.branch}${path}`;
},
}); });

View File

@ -1,292 +0,0 @@
import store from '../../store';
import githubHelper from './helpers/githubHelper';
import Provider from './common/Provider';
import gitWorkspaceSvc from '../gitWorkspaceSvc';
import userSvc from '../userSvc';
const appDataRepo = 'stackedit-app-data';
const appDataBranch = 'master';
export default new Provider({
id: 'githubAppData',
name: 'Gitee应用数据',
getToken() {
return store.getters['workspace/syncToken'];
},
getWorkspaceParams() {
// No param as it's the main workspace
return {};
},
getWorkspaceLocationUrl() {
// No direct link to app data
return null;
},
getSyncDataUrl() {
// No direct link to app data
return null;
},
getSyncDataDescription({ id }) {
return id;
},
async initWorkspace() {
// Nothing much to do since the main workspace isn't necessarily synchronized
// Return the main workspace
return store.getters['workspace/workspacesById'].main;
},
getChanges() {
const token = this.getToken();
return githubHelper.getTree({
owner: token.name,
repo: appDataRepo,
branch: appDataBranch,
token,
});
},
prepareChanges(tree) {
return gitWorkspaceSvc.makeChanges(tree);
},
async saveWorkspaceItem({ item }) {
const syncData = {
id: store.getters.gitPathsByItemId[item.id],
type: item.type,
hash: item.hash,
};
// Files and folders are not in git, only contents
if (item.type === 'file' || item.type === 'folder') {
return { syncData };
}
// locations are stored as paths, so we upload an empty file
const syncToken = store.getters['workspace/syncToken'];
await githubHelper.uploadFile({
owner: syncToken.name,
repo: appDataRepo,
branch: appDataBranch,
token: syncToken,
path: syncData.id,
content: '',
sha: gitWorkspaceSvc.shaByPath[syncData.id],
commitMessage: item.commitMessage,
});
// Return sync data to save
return { syncData };
},
async removeWorkspaceItem({ syncData }) {
if (gitWorkspaceSvc.shaByPath[syncData.id]) {
const syncToken = store.getters['workspace/syncToken'];
await githubHelper.removeFile({
owner: syncToken.name,
repo: appDataRepo,
branch: appDataBranch,
token: syncToken,
path: syncData.id,
sha: gitWorkspaceSvc.shaByPath[syncData.id],
});
}
},
async downloadWorkspaceContent({
token,
contentId,
contentSyncData,
fileSyncData,
}) {
const { sha, data } = await githubHelper.downloadFile({
owner: token.name,
repo: appDataRepo,
branch: appDataBranch,
token,
path: fileSyncData.id,
});
gitWorkspaceSvc.shaByPath[fileSyncData.id] = sha;
const content = Provider.parseContent(data, contentId);
return {
content,
contentSyncData: {
...contentSyncData,
hash: content.hash,
sha,
},
};
},
async downloadFile({ token, path }) {
const { sha, data } = await githubHelper.downloadFile({
owner: token.name,
repo: appDataRepo,
branch: appDataBranch,
token,
path,
isImg: true,
});
return {
content: data,
sha,
};
},
async downloadWorkspaceData({ token, syncData }) {
if (!syncData) {
return {};
}
const path = `.stackedit-data/${syncData.id}.json`;
// const path = store.getters.gitPathsByItemId[syncData.id];
// const path = syncData.id;
const { sha, data } = await githubHelper.downloadFile({
owner: token.name,
repo: appDataRepo,
branch: appDataBranch,
token,
path,
});
if (!sha) {
return {};
}
gitWorkspaceSvc.shaByPath[path] = sha;
const item = JSON.parse(data);
return {
item,
syncData: {
...syncData,
hash: item.hash,
sha,
type: 'data',
},
};
},
async uploadWorkspaceContent({
token,
content,
file,
commitMessage,
}) {
const isImg = file.type === 'img';
const path = !isImg ? store.getters.gitPathsByItemId[file.id] : file.path;
const res = await githubHelper.uploadFile({
owner: token.name,
repo: appDataRepo,
branch: appDataBranch,
token,
path,
content: !isImg ? Provider.serializeContent(content) : file.content,
sha: gitWorkspaceSvc.shaByPath[!isImg ? path : file.path],
isImg,
commitMessage,
});
if (isImg) {
return {
sha: res.content.sha,
};
}
// Return new sync data
return {
contentSyncData: {
id: store.getters.gitPathsByItemId[content.id],
type: content.type,
hash: content.hash,
sha: res.content.sha,
},
fileSyncData: {
id: path,
type: 'file',
hash: file.hash,
},
};
},
async uploadWorkspaceData({
token,
item,
syncData,
}) {
const path = `.stackedit-data/${item.id}.json`;
// const path = store.getters.gitPathsByItemId[item.id];
// const path = syncData.id;
const res = await githubHelper.uploadFile({
token,
owner: token.name,
repo: appDataRepo,
branch: appDataBranch,
path,
content: JSON.stringify(item),
sha: gitWorkspaceSvc.shaByPath[path],
});
return {
syncData: {
...syncData,
type: item.type,
hash: item.hash,
data: item.data,
sha: res.content.sha,
},
};
},
async listFileRevisions({ token, fileSyncDataId }) {
const { owner, repo, branch } = {
owner: token.name,
repo: appDataRepo,
branch: appDataBranch,
};
const entries = await githubHelper.getCommits({
token,
owner,
repo,
sha: branch,
path: fileSyncDataId,
});
return entries.map(({
author,
committer,
commit,
sha,
}) => {
let user;
if (author && author.login) {
user = author;
} else if (committer && committer.login) {
user = committer;
}
const sub = `${githubHelper.subPrefix}:${user.login}`;
if (user.avatar_url && user.avatar_url.endsWith('.png') && !user.avatar_url.endsWith('no_portrait.png')) {
user.avatar_url = `${user.avatar_url}!avatar60`;
}
userSvc.addUserInfo({ id: sub, name: user.login, imageUrl: user.avatar_url });
const date = (commit.author && commit.author.date)
|| (commit.committer && commit.committer.date)
|| 1;
return {
id: sha,
sub,
message: commit.message,
created: new Date(date).getTime(),
};
});
},
async loadFileRevision() {
// Revisions are already loaded
return false;
},
async getFileRevisionContent({
token,
contentId,
fileSyncDataId,
revisionId,
}) {
const { data } = await githubHelper.downloadFile({
owner: token.name,
repo: appDataRepo,
branch: revisionId,
token,
path: fileSyncDataId,
});
return Provider.parseContent(data, contentId);
},
getFilePathUrl(path) {
const token = this.getToken();
if (!token) {
return null;
}
return `https://github.com/${token.name}/${appDataRepo}/blob/${appDataBranch}${path}`;
},
});

View File

@ -306,8 +306,4 @@ export default new Provider({
}); });
return Provider.parseContent(data, contentId); return Provider.parseContent(data, contentId);
}, },
getFilePathUrl(path) {
const workspace = store.getters['workspace/currentWorkspace'];
return `https://github.com/${workspace.owner}/${workspace.repo}/blob/${workspace.branch}${path}`;
},
}); });

View File

@ -75,11 +75,11 @@ export default new Provider({
const sub = workspace ? workspace.sub : utils.queryParams.sub; const sub = workspace ? workspace.sub : utils.queryParams.sub;
let token = store.getters['data/gitlabTokensBySub'][sub]; let token = store.getters['data/gitlabTokensBySub'][sub];
if (!token) { if (!token) {
const { applicationId, applicationSecret } = await store.dispatch('modal/open', { const { applicationId } = await store.dispatch('modal/open', {
type: 'gitlabAccount', type: 'gitlabAccount',
forceServerUrl: serverUrl, forceServerUrl: serverUrl,
}); });
token = await gitlabHelper.addAccount(serverUrl, applicationId, applicationSecret, sub); token = await gitlabHelper.addAccount(serverUrl, applicationId, sub);
} }
if (!workspace) { if (!workspace) {
@ -173,18 +173,6 @@ export default new Provider({
}, },
}; };
}, },
async downloadFile({ token, path }) {
const { sha, data } = await gitlabHelper.downloadFile({
...store.getters['workspace/currentWorkspace'],
token,
path,
isImg: true,
});
return {
content: data,
sha,
};
},
async downloadWorkspaceData({ token, syncData }) { async downloadWorkspaceData({ token, syncData }) {
if (!syncData) { if (!syncData) {
return {}; return {};
@ -212,27 +200,18 @@ export default new Provider({
file, file,
commitMessage, commitMessage,
}) { }) {
const isImg = file.type === 'img';
const path = store.getters.gitPathsByItemId[file.id]; const path = store.getters.gitPathsByItemId[file.id];
const absolutePath = !isImg ? `${store.getters['workspace/currentWorkspace'].path || ''}${path}` : file.path; const absolutePath = `${store.getters['workspace/currentWorkspace'].path || ''}${path}`;
const sha = gitWorkspaceSvc.shaByPath[!isImg ? path : file.path]; const sha = gitWorkspaceSvc.shaByPath[path];
await gitlabHelper.uploadFile({ await gitlabHelper.uploadFile({
...store.getters['workspace/currentWorkspace'], ...store.getters['workspace/currentWorkspace'],
token, token,
path: absolutePath, path: absolutePath,
content: !isImg ? Provider.serializeContent(content) : file.content, content: Provider.serializeContent(content),
sha, sha,
isImg,
commitMessage, commitMessage,
}); });
if (isImg) {
const res2 = await this.downloadFile({ token, path: absolutePath });
return {
sha: res2.sha,
};
}
// Return new sync data // Return new sync data
return { return {
contentSyncData: { contentSyncData: {

View File

@ -317,12 +317,6 @@ export default {
isImg, isImg,
commitMessage, commitMessage,
}) { }) {
// 非法的文件名 不让提交
if (!path || path.endsWith('undefined')) {
return new Promise((resolve) => {
resolve({ res: { content: { sha: null } } });
});
}
let uploadContent = content; let uploadContent = content;
if (isImg && typeof content !== 'string') { if (isImg && typeof content !== 'string') {
uploadContent = await utils.encodeFiletoBase64(content); uploadContent = await utils.encodeFiletoBase64(content);

View File

@ -147,7 +147,6 @@ export default {
sub: `${user.login}`, sub: `${user.login}`,
}; };
if (isMain) { if (isMain) {
token.providerId = 'giteeAppData';
// 检查 stackedit-app-data 仓库是否已经存在 如果不存在则创建该仓库 // 检查 stackedit-app-data 仓库是否已经存在 如果不存在则创建该仓库
await this.checkAndCreateRepo(token); await this.checkAndCreateRepo(token);
} }
@ -280,12 +279,6 @@ export default {
isImg, isImg,
commitMessage, commitMessage,
}) { }) {
// 非法的文件名 不让提交
if (!path || path.endsWith('undefined')) {
return new Promise((resolve) => {
resolve({ res: { content: { sha: null } } });
});
}
let uploadContent = content; let uploadContent = content;
if (isImg && typeof content !== 'string') { if (isImg && typeof content !== 'string') {
uploadContent = await utils.encodeFiletoBase64(content); uploadContent = await utils.encodeFiletoBase64(content);
@ -353,8 +346,8 @@ export default {
}, },
/** /**
* https://gitee.com/api/v5/swagger#/postV5Gists * https://developer.gitee.com/v3/gists/#create-a-gist
* https://gitee.com/api/v5/swagger#/patchV5GistsId * https://developer.gitee.com/v3/gists/#edit-a-gist
*/ */
async uploadGist({ async uploadGist({
token, token,
@ -364,7 +357,8 @@ export default {
isPublic, isPublic,
gistId, gistId,
}) { }) {
const { body } = await request(token, gistId ? { const refreshedToken = await this.refreshToken(token);
const { body } = await request(refreshedToken, gistId ? {
method: 'PATCH', method: 'PATCH',
url: `https://gitee.com/api/v5/gists/${gistId}`, url: `https://gitee.com/api/v5/gists/${gistId}`,
body: { body: {
@ -392,15 +386,16 @@ export default {
}, },
/** /**
* https://gitee.com/api/v5/swagger#/getV5Gists * https://developer.gitee.com/v3/gists/#get-a-single-gist
*/ */
async downloadGist({ async downloadGist({
token, token,
gistId, gistId,
filename, filename,
}) { }) {
const result = (await request(token, { const refreshedToken = await this.refreshToken(token);
url: `https://api.github.com/gists/${gistId}`, const result = (await request(refreshedToken, {
url: `https://gitee.com/api/v5/gists/${gistId}`,
})).body.files[filename]; })).body.files[filename];
if (!result) { if (!result) {
throw new Error('Gist file not found.'); throw new Error('Gist file not found.');
@ -409,15 +404,35 @@ export default {
}, },
/** /**
* https://gitee.com/api/v5/swagger#/getV5GistsIdCommits * https://developer.gitee.com/v3/gists/#list-gist-commits
*/ */
async getGistCommits({ async getGistCommits({
token, token,
gistId, gistId,
}) { }) {
const { body } = await request(token, { const refreshedToken = await this.refreshToken(token);
const { body } = await request(refreshedToken, {
url: `https://gitee.com/api/v5/gists/${gistId}/commits`, url: `https://gitee.com/api/v5/gists/${gistId}/commits`,
}); });
return body; return body;
}, },
/**
* https://developer.gitee.com/v3/gists/#get-a-specific-revision-of-a-gist
*/
async downloadGistRevision({
token,
gistId,
filename,
sha,
}) {
const refreshedToken = await this.refreshToken(token);
const result = (await request(refreshedToken, {
url: `https://gitee.com/api/v5/gists/${gistId}/${sha}`,
})).body.files[filename];
if (!result) {
throw new Error('Gist file not found.');
}
return result.content;
},
}; };

View File

@ -6,8 +6,6 @@ import badgeSvc from '../../badgeSvc';
const getScopes = token => [token.repoFullAccess ? 'repo' : 'public_repo', 'gist']; const getScopes = token => [token.repoFullAccess ? 'repo' : 'public_repo', 'gist'];
const appDataRepo = 'stackedit-app-data';
const request = (token, options) => networkSvc.request({ const request = (token, options) => networkSvc.request({
...options, ...options,
headers: { headers: {
@ -64,7 +62,7 @@ export default {
/** /**
* https://developer.github.com/apps/building-oauth-apps/authorization-options-for-oauth-apps/ * https://developer.github.com/apps/building-oauth-apps/authorization-options-for-oauth-apps/
*/ */
async startOauth2(scopes, sub = null, silent = false, isMain) { async startOauth2(scopes, sub = null, silent = false) {
await networkSvc.getServerConf(); await networkSvc.getServerConf();
const clientId = store.getters['data/serverConf'].githubClientId; const clientId = store.getters['data/serverConf'].githubClientId;
@ -112,26 +110,16 @@ export default {
const token = { const token = {
scopes, scopes,
accessToken, accessToken,
// 主文档空间的登录 标识登录
isLogin: !!isMain || (oldToken && !!oldToken.isLogin),
name: user.login, name: user.login,
sub: `${user.id}`, sub: `${user.id}`,
imgStorages: oldToken && oldToken.imgStorages, imgStorages: oldToken && oldToken.imgStorages,
repoFullAccess: scopes.includes('repo'), repoFullAccess: scopes.includes('repo'),
}; };
if (isMain) {
token.providerId = 'githubAppData';
// check stackedit-app-data repo exist?
await this.checkAndCreateRepo(token);
}
// Add token to github tokens // Add token to github tokens
store.dispatch('data/addGithubToken', token); store.dispatch('data/addGithubToken', token);
return token; return token;
}, },
signin() {
return this.startOauth2(['repo', 'gist'], null, false, true);
},
async addAccount(repoFullAccess = false) { async addAccount(repoFullAccess = false) {
const token = await this.startOauth2(getScopes({ repoFullAccess })); const token = await this.startOauth2(getScopes({ repoFullAccess }));
badgeSvc.addBadge('addGitHubAccount'); badgeSvc.addBadge('addGitHubAccount');
@ -160,30 +148,6 @@ export default {
return tree; return tree;
}, },
async checkAndCreateRepo(token) {
const url = `https://api.github.com/repos/${encodeURIComponent(token.name)}/${encodeURIComponent(appDataRepo)}`;
try {
await request(token, { url });
} catch (err) {
// create
if (err.status === 404) {
await request(token, {
method: 'POST',
url: 'https://api.github.com/repos/mafgwo/stackedit-appdata-template/generate',
body: {
owner: token.name,
name: appDataRepo,
description: 'StackEdit中文版默认空间.',
include_all_branches: false,
private: true,
},
});
} else {
throw err;
}
}
},
/** /**
* https://developer.github.com/v3/repos/commits/#list-commits-on-a-repository * https://developer.github.com/v3/repos/commits/#list-commits-on-a-repository
*/ */
@ -193,29 +157,11 @@ export default {
repo, repo,
sha, sha,
path, path,
tryTimes,
}) { }) {
let tryCount = tryTimes || 1;
try {
return repoRequest(token, owner, repo, { return repoRequest(token, owner, repo, {
url: 'commits', url: 'commits',
params: { sha, path }, params: { sha, path },
}); });
} catch (err) {
// 主文档 并且 409 则重试3次
if (tryCount <= 3 && err.status === 409 && repo === appDataRepo) {
tryCount += 1;
return this.getCommits({
token,
owner,
repo,
sha,
path,
tryTimes: tryCount,
});
}
throw err;
}
}, },
/** /**
@ -233,12 +179,6 @@ export default {
isImg, isImg,
commitMessage, commitMessage,
}) { }) {
// 非法的文件名 不让提交
if (!path || path.endsWith('undefined')) {
return new Promise((resolve) => {
resolve({ res: { content: { sha: null } } });
});
}
let uploadContent = content; let uploadContent = content;
if (isImg && typeof content !== 'string') { if (isImg && typeof content !== 'string') {
uploadContent = await utils.encodeFiletoBase64(content); uploadContent = await utils.encodeFiletoBase64(content);
@ -288,30 +228,14 @@ export default {
path, path,
isImg, isImg,
}) { }) {
try { const { sha, content } = await repoRequest(token, owner, repo, {
const { sha, content, encoding } = await repoRequest(token, owner, repo, {
url: `contents/${encodeURIComponent(path)}`, url: `contents/${encodeURIComponent(path)}`,
params: { ref: branch }, params: { ref: branch },
}); });
let tempContent = content;
// 如果是图片且 encoding 为 none 则 需要获取 blob
if (isImg && encoding === 'none') {
const blobInfo = await repoRequest(token, owner, repo, {
url: `git/blobs/${sha}`,
});
tempContent = blobInfo.content;
}
return { return {
sha, sha,
data: !isImg ? utils.decodeBase64(tempContent) : tempContent, data: !isImg ? utils.decodeBase64(content) : content,
}; };
} catch (err) {
// not .stackedit-data throw err
if (err.status === 404 && path.indexOf('.stackedit-data') >= 0) {
return {};
}
throw err;
}
}, },
/** /**
* 获取仓库信息 * 获取仓库信息

View File

@ -3,9 +3,6 @@ import networkSvc from '../../networkSvc';
import store from '../../../store'; import store from '../../../store';
import userSvc from '../../userSvc'; import userSvc from '../../userSvc';
import badgeSvc from '../../badgeSvc'; import badgeSvc from '../../badgeSvc';
import constants from '../../../data/constants';
const tokenExpirationMargin = 5 * 60 * 1000;
const request = ({ accessToken, serverUrl }, options) => networkSvc.request({ const request = ({ accessToken, serverUrl }, options) => networkSvc.request({
...options, ...options,
@ -53,90 +50,23 @@ export default {
/** /**
* https://docs.gitlab.com/ee/api/oauth2.html * https://docs.gitlab.com/ee/api/oauth2.html
*/ */
async startOauth2( async startOauth2(serverUrl, applicationId, sub = null, silent = false) {
serverUrl, applicationId, applicationSecret,
sub = null, silent = false, refreshToken,
) {
let apiUrl = serverUrl;
let clientId = applicationId;
let useServerConf = false;
// 获取gitlab配置的参数
await networkSvc.getServerConf();
const confClientId = store.getters['data/serverConf'].gitlabClientId;
const confServerUrl = store.getters['data/serverConf'].gitlabUrl;
// 存在gitlab配置则使用后端配置
if (confClientId && confServerUrl) {
apiUrl = confServerUrl;
clientId = confClientId;
useServerConf = true;
}
let tokenBody;
if (!silent) {
// Get an OAuth2 code // Get an OAuth2 code
const { code } = await networkSvc.startOauth2( const { accessToken } = await networkSvc.startOauth2(
`${apiUrl}/oauth/authorize`, `${serverUrl}/oauth/authorize`,
{ {
client_id: clientId, client_id: applicationId,
response_type: 'code', response_type: 'token',
redirect_uri: constants.oauth2RedirectUri, scope: 'api',
}, },
silent, silent,
); );
if (useServerConf) {
tokenBody = (await networkSvc.request({
method: 'GET',
url: 'oauth2/gitlabToken',
params: {
code,
grant_type: 'authorization_code',
redirect_uri: constants.oauth2RedirectUri,
},
})).body;
} else {
// Exchange code with token
tokenBody = (await networkSvc.request({
method: 'POST',
url: `${apiUrl}/oauth/token`,
params: {
client_id: clientId,
client_secret: applicationSecret,
code,
grant_type: 'authorization_code',
redirect_uri: constants.oauth2RedirectUri,
},
})).body;
}
} else if (useServerConf) {
tokenBody = (await networkSvc.request({
method: 'GET',
url: 'oauth2/gitlabToken',
params: {
refresh_token: refreshToken,
grant_type: 'refresh_token',
redirect_uri: constants.oauth2RedirectUri,
},
})).body;
} else {
// Exchange refreshToken with token
tokenBody = (await networkSvc.request({
method: 'POST',
url: `${apiUrl}/oauth/token`,
body: {
client_id: clientId,
client_secret: applicationSecret,
refresh_token: refreshToken,
grant_type: 'refresh_token',
redirect_uri: constants.oauth2RedirectUri,
},
})).body;
}
const accessToken = tokenBody.access_token;
// Call the user info endpoint // Call the user info endpoint
const user = await request({ accessToken, serverUrl: apiUrl }, { const user = await request({ accessToken, serverUrl }, {
url: 'user', url: 'user',
}); });
const uniqueSub = `${apiUrl}/${user.id}`; const uniqueSub = `${serverUrl}/${user.id}`;
userSvc.addUserInfo({ userSvc.addUserInfo({
id: `${subPrefix}:${uniqueSub}`, id: `${subPrefix}:${uniqueSub}`,
name: user.username, name: user.username,
@ -148,17 +78,11 @@ export default {
throw new Error('GitLab account ID not expected.'); throw new Error('GitLab account ID not expected.');
} }
const oldToken = store.getters['data/gitlabTokensBySub'][uniqueSub];
// Build token object including scopes and sub // Build token object including scopes and sub
const token = { const token = {
accessToken, accessToken,
name: user.username, name: user.username,
applicationId: clientId, serverUrl,
applicationSecret,
imgStorages: oldToken && oldToken.imgStorages,
refreshToken: tokenBody.refresh_token,
expiresOn: Date.now() + ((tokenBody.expires_in || 7200) * 1000),
serverUrl: apiUrl,
sub: uniqueSub, sub: uniqueSub,
}; };
@ -166,58 +90,12 @@ export default {
store.dispatch('data/addGitlabToken', token); store.dispatch('data/addGitlabToken', token);
return token; return token;
}, },
async addAccount(serverUrl, applicationId, applicationSecret, sub = null) { async addAccount(serverUrl, applicationId, sub = null) {
const token = await this.startOauth2(serverUrl, applicationId, applicationSecret, sub); const token = await this.startOauth2(serverUrl, applicationId, sub);
badgeSvc.addBadge('addGitLabAccount'); badgeSvc.addBadge('addGitLabAccount');
return token; return token;
}, },
// 刷新token
async refreshToken(token) {
const {
serverUrl,
applicationId,
applicationSecret,
sub,
} = token;
const lastToken = store.getters['data/gitlabTokensBySub'][sub];
// 兼容旧的没有过期时间
if (!lastToken.expiresOn || !lastToken.refreshToken) {
await store.dispatch('modal/open', {
type: 'providerRedirection',
name: 'Gitlab',
});
return this.startOauth2(serverUrl, applicationId, applicationSecret, sub);
}
// lastToken is not expired
if (lastToken.expiresOn > Date.now() + tokenExpirationMargin) {
return lastToken;
}
// existing token is about to expire.
// Try to get a new token in background
try {
return await this.startOauth2(
serverUrl, applicationId, applicationSecret,
sub, true, lastToken.refreshToken,
);
} catch (err) {
// If it fails try to popup a window
if (store.state.offline) {
throw err;
}
await store.dispatch('modal/open', {
type: 'providerRedirection',
name: 'Gitlab',
});
return this.startOauth2(serverUrl, applicationId, applicationSecret, sub);
}
},
// 带刷新token
async requestWithRefreshToken(token, options) {
const refreshedToken = await this.refreshToken(token);
const result = await request(refreshedToken, options);
return result;
},
/** /**
* https://docs.gitlab.com/ee/api/projects.html#get-single-project * https://docs.gitlab.com/ee/api/projects.html#get-single-project
*/ */
@ -225,7 +103,8 @@ export default {
if (projectId) { if (projectId) {
return projectId; return projectId;
} }
const project = await this.requestWithRefreshToken(token, {
const project = await request(token, {
url: `projects/${encodeURIComponent(projectPath)}`, url: `projects/${encodeURIComponent(projectPath)}`,
}); });
return project.id; return project.id;
@ -239,7 +118,7 @@ export default {
projectId, projectId,
branch, branch,
}) { }) {
return this.requestWithRefreshToken(token, { return request(token, {
url: `projects/${encodeURIComponent(projectId)}/repository/tree`, url: `projects/${encodeURIComponent(projectId)}/repository/tree`,
params: { params: {
ref: branch, ref: branch,
@ -258,7 +137,7 @@ export default {
branch, branch,
path, path,
}) { }) {
return this.requestWithRefreshToken(token, { return request(token, {
url: `projects/${encodeURIComponent(projectId)}/repository/commits`, url: `projects/${encodeURIComponent(projectId)}/repository/commits`,
params: { params: {
ref_name: branch, ref_name: branch,
@ -278,26 +157,14 @@ export default {
path, path,
content, content,
sha, sha,
isImg,
commitMessage, commitMessage,
}) { }) {
// 非法的文件名 不让提交 return request(token, {
if (!path || path.endsWith('undefined')) {
return new Promise((resolve) => {
resolve({ res: { content: { sha: null } } });
});
}
let uploadContent = content;
if (isImg && typeof content !== 'string') {
uploadContent = await utils.encodeFiletoBase64(content);
}
return this.requestWithRefreshToken(token, {
method: sha ? 'PUT' : 'POST', method: sha ? 'PUT' : 'POST',
url: `projects/${encodeURIComponent(projectId)}/repository/files/${encodeURIComponent(path)}`, url: `projects/${encodeURIComponent(projectId)}/repository/files/${encodeURIComponent(path)}`,
body: { body: {
commit_message: commitMessage || getCommitMessage(sha ? 'updateFileMessage' : 'createFileMessage', path), commit_message: commitMessage || getCommitMessage(sha ? 'updateFileMessage' : 'createFileMessage', path),
encoding: 'base64', content,
content: isImg ? uploadContent : utils.encodeBase64(content),
last_commit_id: sha, last_commit_id: sha,
branch, branch,
}, },
@ -314,7 +181,7 @@ export default {
path, path,
sha, sha,
}) { }) {
return this.requestWithRefreshToken(token, { return request(token, {
method: 'DELETE', method: 'DELETE',
url: `projects/${encodeURIComponent(projectId)}/repository/files/${encodeURIComponent(path)}`, url: `projects/${encodeURIComponent(projectId)}/repository/files/${encodeURIComponent(path)}`,
body: { body: {
@ -333,15 +200,14 @@ export default {
projectId, projectId,
branch, branch,
path, path,
isImg,
}) { }) {
const res = await this.requestWithRefreshToken(token, { const res = await request(token, {
url: `projects/${encodeURIComponent(projectId)}/repository/files/${encodeURIComponent(path)}`, url: `projects/${encodeURIComponent(projectId)}/repository/files/${encodeURIComponent(path)}`,
params: { ref: branch }, params: { ref: branch },
}); });
return { return {
sha: res.last_commit_id, sha: res.last_commit_id,
data: !isImg ? utils.decodeBase64(res.content) : res.content, data: utils.decodeBase64(res.content),
}; };
}, },
}; };

View File

@ -10,7 +10,8 @@ const tokenExpirationMargin = 5 * 60 * 1000; // 5 min (tokens expire after 1h)
const driveAppDataScopes = ['https://www.googleapis.com/auth/drive.appdata']; const driveAppDataScopes = ['https://www.googleapis.com/auth/drive.appdata'];
const getDriveScopes = token => [token.driveFullAccess const getDriveScopes = token => [token.driveFullAccess
? 'https://www.googleapis.com/auth/drive' ? 'https://www.googleapis.com/auth/drive'
: 'https://www.googleapis.com/auth/drive.file']; : 'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive.install'];
const bloggerScopes = ['https://www.googleapis.com/auth/blogger']; const bloggerScopes = ['https://www.googleapis.com/auth/blogger'];
const photosScopes = ['https://www.googleapis.com/auth/photos']; const photosScopes = ['https://www.googleapis.com/auth/photos'];
@ -110,7 +111,6 @@ export default {
* https://developers.google.com/identity/protocols/OpenIDConnect * https://developers.google.com/identity/protocols/OpenIDConnect
*/ */
async startOauth2(scopes, sub = null, silent = false) { async startOauth2(scopes, sub = null, silent = false) {
await networkSvc.getServerConf();
const clientId = store.getters['data/serverConf'].googleClientId; const clientId = store.getters['data/serverConf'].googleClientId;
// Get an OAuth2 code // Get an OAuth2 code
@ -625,7 +625,7 @@ export default {
async openPicker(token, type = 'doc') { async openPicker(token, type = 'doc') {
const scopes = type === 'img' ? photosScopes : getDriveScopes(token); const scopes = type === 'img' ? photosScopes : getDriveScopes(token);
if (!window.google) { if (!window.google) {
await networkSvc.loadScript('https://apis.google.com/js/api.js'); await networkSvc.loadScript('https://www.gstatic.cn/charts/loader.js');
await new Promise((resolve, reject) => window.gapi.load('picker', { await new Promise((resolve, reject) => window.gapi.load('picker', {
callback: resolve, callback: resolve,
onerror: reject, onerror: reject,

View File

@ -139,12 +139,6 @@ const requestPublish = () => {
}); });
}; };
const publishLocationAndStore = async (publishLocation, commitMsg) => {
const publishLocationToStore = await publish(publishLocation, commitMsg);
workspaceSvc.addPublishLocation(publishLocationToStore);
return publishLocationToStore;
};
const createPublishLocation = (publishLocation, featureId) => { const createPublishLocation = (publishLocation, featureId) => {
const currentFile = store.getters['file/current']; const currentFile = store.getters['file/current'];
publishLocation.fileId = currentFile.id; publishLocation.fileId = currentFile.id;
@ -163,7 +157,8 @@ const createPublishLocation = (publishLocation, featureId) => {
return; return;
} }
} }
await publishLocationAndStore(publishLocation, commitMsg); const publishLocationToStore = await publish(publishLocation, commitMsg);
workspaceSvc.addPublishLocation(publishLocationToStore);
store.dispatch('notification/info', `添加了一个新的发布位置 "${currentFile.name}".`); store.dispatch('notification/info', `添加了一个新的发布位置 "${currentFile.name}".`);
if (featureId) { if (featureId) {
badgeSvc.addBadge(featureId); badgeSvc.addBadge(featureId);
@ -174,6 +169,5 @@ const createPublishLocation = (publishLocation, featureId) => {
export default { export default {
requestPublish, requestPublish,
publishLocationAndStore,
createPublishLocation, createPublishLocation,
}; };

Some files were not shown because too many files have changed in this diff Show More