Compare commits

...

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

799 changed files with 74428 additions and 67377 deletions

View File

@ -1,14 +0,0 @@
{
"presets": [
["env", { "modules": false }],
"stage-2"
],
"plugins": ["transform-runtime"],
"comments": false,
"env": {
"test": {
"presets": ["env", "stage-2"],
"plugins": ["transform-es2015-modules-commonjs", "dynamic-import-node"]
}
}
}

3
.bowerrc Normal file
View File

@ -0,0 +1,3 @@
{
"directory": "public/res/bower-libs"
}

View File

@ -1,9 +0,0 @@
node_modules
.git
dist
.history
images
docs
Dockerfile
README.md
build.sh

View File

@ -1,9 +1,9 @@
# http://EditorConfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
indent_size = 4
indent_style = tab
insert_final_newline = true
trim_trailing_whitespace = true

View File

@ -1,3 +0,0 @@
build/*.js
config/*.js
src/libs/*.js

View File

@ -1,44 +0,0 @@
// http://eslint.org/docs/user-guide/configuring
module.exports = {
root: true,
parser: 'babel-eslint',
parserOptions: {
sourceType: 'module'
},
env: {
browser: true,
},
extends: 'airbnb-base',
// required to lint *.vue files
plugins: [
'html'
],
globals: {
"NODE_ENV": false,
"VERSION": false
},
// check if imports actually resolve
'settings': {
'import/resolver': {
'webpack': {
'config': 'build/webpack.base.conf.js'
}
}
},
// add your custom rules here
'rules': {
'no-param-reassign': [2, { 'props': false }],
// don't require .vue extension when importing
'import/extensions': ['error', 'always', {
'js': 'never',
'vue': 'never'
}],
// allow optionalDependencies
'import/no-extraneous-dependencies': ['error', {
'optionalDependencies': ['test/unit/index.js']
}],
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
}
}

16
.gitignore vendored
View File

@ -1,10 +1,8 @@
.DS_Store
node_modules/
dist/
.history
.project
.idea
npm-debug.log*
.vscode
stackedit_v4
chrome-app/*.zip
/test/unit/coverage/
.settings
node_modules
Thumbs.db
.DS_Store
stackedit.iml
public/res/bower-libs

9
.jshintrc Normal file
View File

@ -0,0 +1,9 @@
{
"curly": true,
"node": true,
"indent": 4,
"latedef": true,
"undef": true,
"unused": true,
"expr": true
}

View File

@ -1,8 +0,0 @@
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
"plugins": {
// to edit target browsers: use "browserlist" field in package.json
"autoprefixer": {}
}
}

View File

@ -1,7 +0,0 @@
{
"processors": ["stylelint-processor-html"],
"extends": "stylelint-config-standard",
"rules": {
"no-empty-source": null
}
}

View File

@ -1,22 +0,0 @@
language: node_js
node_js:
- "12"
services:
- docker
before_deploy:
# Run docker build
- docker build -t benweet/stackedit .
# Install Helm
- curl -SL -o /tmp/get_helm.sh https://git.io/get_helm.sh
- chmod 700 /tmp/get_helm.sh
- /tmp/get_helm.sh
- helm init --client-only
deploy:
provider: script
script: bash build/deploy.sh
on:
tags: true

View File

@ -1,16 +1,7 @@
FROM mafgwo/wkhtmltopdf-nodejs:11.15.0
# Pull base image.
FROM node:0.12-onbuild
WORKDIR /opt/stackedit
# Node base will default the command to `node server.js`.
COPY package*json /opt/stackedit/
COPY gulpfile.js /opt/stackedit/
RUN npm install --unsafe-perm \
&& npm cache clean --force
COPY . /opt/stackedit
ENV NODE_ENV production
RUN npm run build
EXPOSE 8080
CMD [ "node", "." ]
# Expose port.
EXPOSE 3000

285
Gulpfile.js Normal file
View File

@ -0,0 +1,285 @@
/* jshint -W015 */
var gulp = require('gulp');
var util = require('gulp-util');
var clean = require('gulp-clean');
var jshint = require('gulp-jshint');
var requirejs = require('gulp-requirejs');
var bowerRequirejs = require('bower-requirejs');
var uglify = require('gulp-uglify');
var less = require('gulp-less');
var inject = require('gulp-inject');
var replace = require('gulp-replace');
var bump = require('gulp-bump');
var childProcess = require('child_process');
var runSequence = require('run-sequence');
var fs = require('fs');
/** __________________________________________
* constants.js
*/
function getVersion() {
var packageJson = JSON.parse(fs.readFileSync(__dirname + '/package.json', {
encoding: 'utf8'
}));
return packageJson.version;
}
gulp.task('constants', function() {
return gulp.src('./public/res/constants.js')
.pipe(replace(/constants\.VERSION = .*/, 'constants.VERSION = "' + getVersion() + '";'))
.pipe(gulp.dest('./public/res/'));
});
/** __________________________________________
* JSHint
*/
gulp.task('jshint', function() {
return gulp.src([
'./*.js',
'./app/**/*.js',
'./public/res/classes/**/*.js',
'./public/res/extensions/**/*.js',
'./public/res/helpers/**/*.js',
'./public/res/providers/**/*.js',
'./public/res/*.js'
])
.pipe(jshint())
.pipe(jshint.reporter('default'))
.pipe(jshint.reporter('fail'));
});
/** __________________________________________
* RequireJS
*/
gulp.task('clean-requirejs', function() {
return gulp.src([
'./public/res-min/main.js',
'./public/res-min/require.js'
])
.pipe(clean());
});
gulp.task('copy-requirejs', ['clean-requirejs'], function() {
return gulp.src('./public/res/bower-libs/requirejs/require.js')
.pipe(gulp.dest('./public/res-min/'));
});
gulp.task('requirejs', [
'copy-requirejs',
'constants'
], function() {
return requirejs({
baseUrl: 'public/res',
name: 'main',
out: 'main.js',
mainConfigFile: 'public/res/main.js',
// optimize: 'uglify2',
inlineText: true,
paths: {
mathjax: 'empty:'
},
excludeShallow: [
'css/css-builder',
'less/lessc-server',
'less/lessc'
]
})
.pipe(uglify({
output: {
beautify: true,
indent_level: 1,
ascii_only: true
}
}))
.pipe(gulp.dest('./public/res-min/'));
});
gulp.task('bower-requirejs', function() {
return new Promise(function (resolve) {
bowerRequirejs({
config: './public/res/main.js'
}, resolve);
})
});
/** __________________________________________
* Less
*/
gulp.task('clean-less', function() {
return gulp.src('./public/res-min/themes')
.pipe(clean());
});
gulp.task('less', ['clean-less'], function() {
return gulp.src([
'./public/res/styles/base.less',
'./public/res/themes/*.less'
])
.pipe(less({
compress: true
}))
.pipe(gulp.dest('./public/res-min/themes/'));
});
/** __________________________________________
* Fonts
*/
gulp.task('clean-font', function() {
return gulp.src('./public/res-min/font')
.pipe(clean());
});
gulp.task('copy-font', ['clean-font'], function() {
return gulp.src('./public/res/font/*')
.pipe(gulp.dest('./public/res-min/font/'));
});
/** __________________________________________
* Images
*/
gulp.task('clean-img', function() {
return gulp.src('./public/res-min/img')
.pipe(clean());
});
gulp.task('copy-img', ['clean-img'], function() {
return gulp.src('./public/res/img/*')
.pipe(gulp.dest('./public/res-min/img/'));
});
/** __________________________________________
* cache.manifest
*/
gulp.task('cache-manifest', function() {
return gulp.src('./public/cache.manifest')
.pipe(replace(/(#Date ).*/, '$1' + Date()))
.pipe(inject(gulp.src([
'./res-min/**/*.*'
], {
read: false,
cwd: './public'
}),
{
starttag: '# start_inject_resources',
endtag: '# end_inject_resources',
ignoreExtensions: true,
transform: function(filepath) {
return filepath.substring(1);
}
}))
.pipe(inject(gulp.src([
'./res/bower-libs/MathJax/MathJax.js',
'./res/bower-libs/MathJax/config/Safe.js',
'./res/bower-libs/MathJax/config/TeX-AMS_SVG.js',
'./res/bower-libs/MathJax/images/CloseX-31.png',
'./res/bower-libs/MathJax/images/MenuArrow-15.png',
'./res/bower-libs/MathJax/jax/output/SVG/jax.js',
'./res/bower-libs/MathJax/extensions/**/*.*',
'./res/bower-libs/MathJax/jax/element/**/*.*',
'./res/bower-libs/MathJax/jax/output/SVG/autoload/**/*.*',
'./res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/**/*.*'
], {
read: false,
cwd: './public'
}),
{
starttag: '# start_inject_mathjax',
endtag: '# end_inject_mathjax',
ignoreExtensions: true,
transform: function(filepath) {
if(filepath == '/res/bower-libs/MathJax/MathJax.js') {
filepath += '?config=TeX-AMS_SVG';
}
else {
filepath += '?rev=2.6.1';
}
return filepath.substring(1);
}
}))
.pipe(gulp.dest('./public/'));
});
gulp.task('clean', [
'clean-requirejs',
'clean-less',
'clean-font',
'clean-img'
]);
gulp.task('default', function(cb) {
runSequence([
// 'jshint',
'requirejs',
'less',
'copy-font',
'copy-img'
],
'cache-manifest',
cb);
});
function bumpTask(importance) {
return function() {
return gulp.src([
'./package.json',
'./bower.json'
])
.pipe(bump({type: importance}))
.pipe(gulp.dest('./'));
};
}
gulp.task('bump-patch', bumpTask('patch'));
gulp.task('bump-minor', bumpTask('minor'));
gulp.task('bump-major', bumpTask('major'));
function exec(cmd) {
return new Promise (function (resolve, reject) {
childProcess.exec(cmd, {cwd: process.cwd()}, function(err, stdout, stderr) {
if (err) {
reject(err);
} else {
util.log(stdout, stderr);
resolve();
}
});
});
}
gulp.task('git-tag', function() {
var tag = 'v' + getVersion();
util.log('Tagging as: ' + util.colors.cyan(tag));
return exec('git add ./public/res-min')
.then(function () {
return exec('git commit -a -m "Prepare release"');
})
.then(function () {
return exec('git tag -a ' + tag + ' -m "Version ' + getVersion() + '"');
})
.then(function () {
return exec('npm publish');
})
.then(function () {
return exec('git push origin v4 --tags');
});
});
function releaseTask(importance) {
return function(cb) {
runSequence(
'bump-' + importance,
'default',
'git-tag',
cb);
};
}
gulp.task('patch', releaseTask('patch'));
gulp.task('minor', releaseTask('minor'));
gulp.task('major', releaseTask('major'));

201
LICENSE
View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

4
LICENSE.txt Normal file
View File

@ -0,0 +1,4 @@
StackEdit - The Markdown editor powered by PageDown.
Copyright 2013 Benoit Schweblin (http://www.benoitschweblin.com)
Licensed under an Apache License (http://www.apache.org/licenses/LICENSE-2.0)

1
Procfile Normal file
View File

@ -0,0 +1 @@
web: node server.js

218
README.md
View File

@ -1,191 +1,55 @@
<h1 align="center" style="text-align:center;">
<img src="chrome-app/icon-512.png" width="128" />
<br />
StackEdit中文版
</h1>
<p align="center">
<strong>笔记利器在线Markdown编辑器。</strong><br>
项目clone自<a href="https://gitee.com/mafgwo/stackedit" target="_blank" title="豆萁">豆萁/stackedit</a>如果你喜欢该项目请过去点一下Star您的肯定是作者最大的动力
</p>
<p align="center">
<a href="https://stackedit.cn/">https://stackedit.cn</a>
</p>
<p align="center">
<a target="_blank" href="https://www.apache.org/licenses/LICENSE-2.0.txt">
<img src="https://img.shields.io/:license-Apache2-blue.svg" alt="Apache 2" />
</a>
<a target="_blank" href="https://hub.docker.com/r/mafgwo/stackedit">
<img src="https://img.shields.io/docker/pulls/mafgwo/stackedit.svg" alt="Docker Pulls" />
</a>
<a target="_blank" href='https://gitee.com/mafgwo/stackedit/stargazers'>
<img src='https://gitee.com/mafgwo/stackedit/badge/star.svg' alt='gitee star'/>
</a>
</p>
<br/>
<hr />
1 笔记支持Gitee、GitHub、Gitea等Git仓库存储。<br>
2 支持直接上传图片也支持多种外部图床GitHub、Gitea、SM.MS、自定义图床粘贴或拖拽上传。<br>
3 编辑区域支持选择主题或自定义,总有你喜欢的主题。<br>
4 支持历史版本管理,不用担心编辑覆盖后无法回滚。<br>
5 支持ChatGPT辅助写作。<br>
6 支持KaTeX数学表达式、Mermaid UML图、乐谱等扩展。
<hr />
StackEdit
=========
## 说明
StackEdit is a full-featured, open-source Markdown editor based on PageDown, the Markdown library used by Stack Overflow and the other Stack Exchange sites.
本项目为本人clone修改自用如果你也喜欢请至原作者处获取及交流。
Main showcase: https://stackedit.io/.
## 截图
Support StackEdit:
**亮暗主题切换、编辑主题切换**
![](./images/theme.gif)
[![](https://cdn.monetizejs.com/resources/button-32.png)](https://monetizejs.com/authorize?client_id=ESTHdCYOi18iLhhO&summary=true)
**支持的文档空间**
![](./images/workspace.png)
> **Note:**
>
> - Documents are stored in the [browser's local storage][1], which means they are not shared between different browsers/computers. Clearing your browser's data may delete all your local documents.
> - Full access to Dropbox or Google Drive is required to be able to import any document in StackEdit. Imported documents are downloaded in your browser and are not transmitted to a server.
**拖拽粘贴上传图片**
![](./images/uploadimg.gif)
### StackEdit can:
**支持文档搜索**
![](./images/search.gif)
- Manage multiple Markdown documents online or offline
- Export your documents in Markdown, HTML or PDF and format it using a template
- Synchronize your Markdown documents in the Cloud
- Edit existing Markdown documents from Google Drive, Dropbox and your local hard drive
- Post your Markdown document on Blogger/Blogspot, WordPress, Tumblr
- Publish your Markdown document on GitHub, Gist, Google Drive, Dropbox or any SSH server
- Share a link to a Markdown document that renders it in a nice viewer
- Show statistics about your document
- Convert HTML to Markdown
**ChatGPT集成协助写作**
![](./images/chatgpt.gif)
### Features:
## 相比国外开源版本的区别:
- Real-time HTML preview with Scroll Link feature to bind editor and preview scrollbars
- Markdown Extra/GitHub Flavored Markdown support and Prettify/Highlight.js syntax highlighting
- LaTeX mathematical expressions using MathJax
- WYSIWYG control buttons
- Configurable layout
- Theming support with different themes available
- A la carte extensions
- Offline editing
- Online synchronization using Google Drive (multi-accounts) and Dropbox
- One click publish on Blogger, Dropbox, Gist, GitHub, Google Drive, SSH server, Tumblr, WordPress
- 修复了Github授权登录问题
- 支持了Gitee仓库2022-05-25
- 支持了Gitea仓库2022-05-25
- 汉化2022-06-01
- 主文档空间从GoogleDrive切换为Gitee2022-06-04
- 支持SM.MS图床粘贴/拖拽图片自动上传2022-07-01
- 支持Gitea图床粘贴/拖拽图片自动上传2022-07-02
- 支持自定义图床粘贴/拖拽图片自动上传2022-07-04
- 支持GitHub图床粘贴/拖拽图片自动上传2022-07-31
- 支持了右上角一键切换主题补全了深色主题的样式2022-08-07
- 编辑与预览区域样式优化2022-08-10
- 左边栏文件资源管理支持搜索文件2022-08-17
- 支持[TOC]目录2022-09-04
- 发布支持填写提交信息[针对Gitee、GitHub、Gitea、Gitlab]2022-09-10
- 支持文档空间关闭自动同步[针对Gitee、GitHub、Gitea、Gitlab]关闭后可自定义提交信息2022-09-23
- Gitea支持后端配置指定应用ID和Secret2022-10-03
- 支持编辑区域选择主题样式2022-10-06
- 支持图片直接存储到当前文档空间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
### Documentation:
## 国外开源版本弊端:
- [Hello! document][2]
- [Developer guide][3]
- [Theming guide][4]
- 作者已经不维护了或很少维护了
- 不支持国内常用Gitee
- 强依赖GoogleDrive而Google Drive在国内不能正常访问
> **NOTE:** This page has been written and published with [StackEdit][5].
## 部署说明
> 建议docker-compose方式部署其他部署方式如遇到问题欢迎提issue。
docker官方仓库下载太慢可以使用阿里云的镜像仓库镜像仓库地址registry.cn-hangzhou.aliyuncs.com/mafgwo/stackedit:【版本号】
`docker-compose.yml`如下:
```yaml
version: "3.7"
services:
stackedit:
image: mafgwo/stackedit:【docker中央仓库找到最新版本】
container_name: stackedit
environment:
- LISTENING_PORT=8080
- ROOT_URL=/
- USER_BUCKET_NAME=root
- DROPBOX_APP_KEY=【不需要支持则删掉】
- DROPBOX_APP_KEY_FULL=【不需要支持则删掉】
- GITHUB_CLIENT_ID=【不需要支持则删掉】
- GITHUB_CLIENT_SECRET=【不需要支持则删掉】
- GITEE_CLIENT_ID=【不需要支持则删掉】
- GITEE_CLIENT_SECRET=【不需要支持则删掉】
- GOOGLE_CLIENT_ID=【不需要支持则删掉】
- GOOGLE_API_KEY=【不需要支持则删掉】
- GITEA_CLIENT_ID=【不需要支持则删掉】
- GITEA_CLIENT_SECRET=【不需要支持则删掉】
- GITEA_URL=【不需要支持则删掉】
- GITLAB_CLIENT_ID=【不需要支持则删掉】
- GITLAB_CLIENT_SECRET=【不需要支持则删掉】
- GITLAB_URL=【不需要支持则删掉】
ports:
- 8080:8080/tcp
network_mode: bridge
restart: always
```
docker-compose方式的启动或停止命令
```bash
# 在 docker-compose.yml 文件目录下 启动命令
docker-compose up -d
# 在 docker-compose.yml 文件目录下 停止命令
docker-compose down
# 更新镜像只需要修改docker-compose.yml中镜像版本执行再停止、启动命令即可
```
或者可以直接通过Docker命名直接启动命令如下
```bash
docker run -itd --name stackedit \
-p 8080:8080 \
-e LISTENING_PORT=8080 \
-e ROOT_URL=/ \
-e USER_BUCKET_NAME=root \
-e DROPBOX_APP_KEY=【不需要支持则删掉】 \
-e DROPBOX_APP_KEY_FULL=【不需要支持则删掉】 \
-e GITHUB_CLIENT_ID=【不需要支持则删掉】 \
-e GITHUB_CLIENT_SECRET=【不需要支持则删掉】 \
-e GITEE_CLIENT_ID=【不需要支持则删掉】 \
-e GITEE_CLIENT_SECRET=【不需要支持则删掉】 \
-e GOOGLE_CLIENT_ID=【不需要支持则删掉】 \
-e GOOGLE_API_KEY=【不需要支持则删掉】 \
-e GITEA_CLIENT_ID=【不需要支持则删掉】 \
-e GITEA_CLIENT_SECRET=【不需要支持则删掉】 \
-e GITEA_URL=【不需要支持则删掉】 \
-e GITLAB_CLIENT_ID=【不需要支持则删掉】 \
-e GITLAB_CLIENT_SECRET=【不需要支持则删掉】 \
-e GITLAB_URL=【不需要支持则删掉】 \
mafgwo/stackedit:【docker中央仓库找到最新版本】
```
## 如何创建三方平台应用
> 部署时如果需要支持Gitee或GitHub则需要自行到对应三方平台创建应用获取到应用ID和秘钥替换到以上的环境变量中再启动应用。
- 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必须支持跨域
## 编译与运行
> 编译运行的nodejs版本选择11.15.0版本
```bash
# 安装依赖
npm install
# serve with hot reload at localhost:8080
npm start
# build for production with minification
npm run build
# build for production and view the bundle analyzer report
npm run build --report
```
[1]: https://developer.mozilla.org/en-US/docs/Web/Guide/DOM/Storage#localStorage
[2]: https://github.com/benweet/stackedit/blob/master/public/res/WELCOME.md#welcome-to-stackedit---welcome "Welcome document"
[3]: https://github.com/benweet/stackedit/blob/master/doc/developer-guide.md#developer-guide "Developer guide"
[4]: https://github.com/benweet/stackedit/blob/master/doc/theming.md#stackedit-theming-guide "Theming guide"
[5]: https://stackedit.io/ "StackEdit"

20
app/download.js Normal file
View File

@ -0,0 +1,20 @@
var request = require('request');
exports.importPublic = function(req, res) {
var url = req.param('url');
if(!url) {
res.send(400, 'No URL parameter');
}
else if(url.indexOf("http://") === 0 || url.indexOf("https://") === 0) {
var stream = request.get(url);
stream.on('error', function(err) {
res.send(400, err);
});
stream.on('response', function() {
stream.pipe(res);
});
}
else {
res.send(400, 'Unknown protocol');
}
};

60
app/index.js Normal file
View File

@ -0,0 +1,60 @@
var express = require('express');
var app = express();
var compression = require('compression');
var serveStatic = require('serve-static');
// Configure ejs engine
app.set('views', __dirname + '/../views');
app.engine('html', require('ejs').renderFile);
// Force HTTPS on stackedit.io
app.all('*', function(req, res, next) {
if (req.headers.host == 'stackedit.io' && !req.secure && req.headers['x-forwarded-proto'] != 'https') {
return res.redirect('https://stackedit.io' + req.url);
}
/\.(eot|ttf|woff|svg)$/.test(req.url) && res.header('Access-Control-Allow-Origin', '*');
next();
});
// Use gzip compression
app.use(compression());
app.post('/pdfExport', require('./pdf').export);
app.post('/sshPublish', require('./ssh').publish);
app.post('/picasaImportImg', require('./picasa').importImg);
app.get('/downloadImport', require('./download').importPublic);
// Serve static resources
app.use(serveStatic(__dirname + '/../public'));
app.use(function(req, res, next) {
res.renderDebug = function(page) {
return res.render(page, {
cache: !req.query.hasOwnProperty('debug')
});
};
next();
});
// Serve landing.html in /
app.get('/', function(req, res) {
res.renderDebug('landing.html');
});
// Serve editor.html in /viewer
app.get('/editor', function(req, res) {
res.renderDebug('editor.html');
});
// Serve viewer.html in /viewer
app.get('/viewer', function(req, res) {
res.renderDebug('viewer.html');
});
// Error 404
app.use(function(req, res) {
res.status(404);
res.render('error_404.html');
});
module.exports = app;

145
app/pdf.js Normal file
View File

@ -0,0 +1,145 @@
/* global window,MathJax */
var spawn = require('child_process').spawn;
var fs = require('fs');
var path = require('path');
var os = require('os');
var request = require('request');
function waitForJavaScript() {
if(window.MathJax) {
// Amazon EC2: fix TeX font detection
MathJax.Hub.Register.StartupHook("HTML-CSS Jax Startup",function () {
var HTMLCSS = MathJax.OutputJax["HTML-CSS"];
HTMLCSS.Font.checkWebFont = function (check,font,callback) {
if (check.time(callback)) {
return;
}
if (check.total === 0) {
HTMLCSS.Font.testFont(font);
setTimeout(check,200);
} else {
callback(check.STATUS.OK);
}
};
});
MathJax.Hub.Queue(function () {
window.status = 'done';
});
}
else {
setTimeout(function() {
window.status = 'done';
}, 2000);
}
}
var authorizedPageSizes = [
'A3',
'A4',
'Legal',
'Letter'
];
exports.export = function(req, res, next) {
function onError(err) {
next(err);
}
function onUnknownError() {
res.statusCode = 400;
res.end('Unknown error');
}
function onUnauthorizedError() {
res.statusCode = 401;
res.end('Unauthorized');
}
function onTimeout() {
res.statusCode = 408;
res.end('Request timeout');
}
request({
uri: 'https://monetizejs.com/api/payments',
qs: {
access_token: req.query.token
},
json: true
}, function (err, paymentsRes, payments) {
var authorized = payments && payments.app == 'ESTHdCYOi18iLhhO' && (
(payments.chargeOption && payments.chargeOption.alias == 'once') ||
(payments.subscriptionOption && payments.subscriptionOption.alias == 'yearly'));
if(err || paymentsRes.statusCode != 200 || !authorized) {
return onUnauthorizedError();
}
var options, params = [];
try {
options = JSON.parse(req.query.options);
}
catch(e) {
options = {};
}
// Margins
var marginTop = parseInt(options.marginTop);
params.push('-T', isNaN(marginTop) ? 25 : marginTop);
var marginRight = parseInt(options.marginRight);
params.push('-R', isNaN(marginRight) ? 25 : marginRight);
var marginBottom = parseInt(options.marginBottom);
params.push('-B', isNaN(marginBottom) ? 25 : marginBottom);
var marginLeft = parseInt(options.marginLeft);
params.push('-L', isNaN(marginLeft) ? 25 : marginLeft);
// Header
options.headerCenter && params.push('--header-center', options.headerCenter);
options.headerLeft && params.push('--header-left', options.headerLeft);
options.headerRight && params.push('--header-right', options.headerRight);
options.headerFontName && params.push('--header-font-name', options.headerFontName);
options.headerFontSize && params.push('--header-font-size', options.headerFontSize);
// Footer
options.footerCenter && params.push('--footer-center', options.footerCenter);
options.footerLeft && params.push('--footer-left', options.footerLeft);
options.footerRight && params.push('--footer-right', options.footerRight);
options.footerFontName && params.push('--footer-font-name', options.footerFontName);
options.footerFontSize && params.push('--footer-font-size', options.footerFontSize);
// Page size
params.push('--page-size', authorizedPageSizes.indexOf(options.pageSize) === -1 ? 'A4' : options.pageSize);
// Use a temp file as wkhtmltopdf can't access /dev/stdout on Amazon EC2 for some reason
var filePath = path.join(os.tmpDir(), Date.now() + '.pdf');
var binPath = process.env.WKHTMLTOPDF_PATH || 'wkhtmltopdf';
params.push('--run-script', waitForJavaScript.toString() + 'waitForJavaScript()');
params.push('--window-status', 'done');
var wkhtmltopdf = spawn(binPath, params.concat('-', filePath), {
stdio: [
'pipe',
'ignore',
'ignore'
]
});
var timeoutId = setTimeout(function() {
timeoutId = undefined;
wkhtmltopdf.kill();
}, 30000);
wkhtmltopdf.on('error', onError);
wkhtmltopdf.stdin.on('error', onError);
wkhtmltopdf.on('close', function(code) {
if(!timeoutId) {
return onTimeout();
}
clearTimeout(timeoutId);
if(code) {
return onUnknownError();
}
var readStream = fs.createReadStream(filePath);
readStream.on('open', function() {
readStream.pipe(res);
});
readStream.on('close', function() {
fs.unlink(filePath, function() {
});
});
readStream.on('error', onUnknownError);
});
req.pipe(wkhtmltopdf.stdin);
});
};

16
app/picasa.js Normal file
View File

@ -0,0 +1,16 @@
var request = require('request');
exports.importImg = function(req, res) {
var stream = req.pipe(request.post({
uri: 'https://picasaweb.google.com/data/feed/api/user/default/albumid/' + req.query.albumId,
headers: {
'Authorization': req.headers.authorization,
'Content-Type': req.headers['content-type'],
'Slug': req.headers.slug
}
}));
stream.on('error', function(err) {
res.send(400, err);
});
stream.pipe(res);
};

62
app/ssh.js Normal file
View File

@ -0,0 +1,62 @@
var ssh2 = require('ssh2');
exports.publish = function(req, res) {
var done;
function sendResult(result) {
if(!done) {
res.json(result);
}
done = true;
}
function sendError(error) {
sendResult({error: error});
}
var conn = new ssh2();
conn.on('ready', function() {
conn.sftp(function(err, sftp) {
if(err) {
return sendError('Unable to establish SFTP connection');
}
var writeStream = sftp.createWriteStream(req.query.path);
writeStream.on('close', function() {
sftp.end();
conn.end();
});
writeStream.on('error', function() {
sendError('Unable to write "' + req.query.path + '"');
sftp.end();
conn.end();
});
req.pipe(writeStream);
});
});
conn.on('error', function(err) {
if(err.level == "authentication") {
return sendError('Authentication failure');
}
if(err.code == "ENOTFOUND") {
return sendError('Host not found');
}
if(err.code == "ETIMEDOUT") {
return sendError('Connection timeout');
}
sendError(err);
});
conn.on('end', function() {
sendResult({});
});
conn.connect({
host: req.query.host,
port: req.query.port || 22,
username: req.query.username,
password: req.query.password
});
};

42
bower.json Normal file
View File

@ -0,0 +1,42 @@
{
"name": "stackedit",
"version": "4.3.22",
"description": "StackEdit is a free, open-source Markdown editor based on PageDown, the Markdown library used by Stack Overflow and the other Stack Exchange sites.",
"dependencies": {
"bootstrap": "3.0.3",
"jquery": "2.0.3",
"underscore": "1.5.1",
"requirejs": "~2.1.11",
"require-css": "0.1.5",
"require-less": "0.1.2",
"mousetrap": "~1.4.4",
"jgrowl": "~1.2.10",
"google-code-prettify": "~1.0.0",
"FileSaver": "*",
"stacktrace": "~0.6.2",
"requirejs-text": "~2.0.10",
"bootstrap-tour": "~0.7.1",
"pagedown-extra": "https://github.com/jmcmanus/pagedown-extra.git#ea782c3d11eb78f57d00aa819527338996a70721",
"crel": "https://github.com/KoryNunn/crel.git#8dbda04b129fc0aec01a2a080d1cab26816e11c1",
"waitForImages": "https://github.com/alexanderdickson/waitForImages.git#~1.4.2",
"to-markdown": "https://github.com/benweet/to-markdown.git#jquery",
"xregexp": "300157f34b39b15b7b0dba7608955945d53e61b9",
"yaml.js": "https://github.com/jeremyfa/yaml.js.git#~0.1.4",
"prism": "a12e8692b5f660a0123a06f74935f75386a93042",
"MutationObservers": "https://github.com/Polymer/MutationObservers.git#~0.2.1",
"rangy": "1.2.3",
"google-diff-match-patch-js": "~1.0.0",
"jsondiffpatch": "https://github.com/benweet/jsondiffpatch.git#fb9dddf7cd076d8ec89d376c0e9de9223e9888f9",
"hammerjs": "~1.0.10",
"raphael": "~2.1.2",
"js-sequence-diagrams": "https://github.com/benweet/js-sequence-diagrams.git#c59e2e39d9185e9291f37b73fc596eba5ed33650",
"flowchart": "https://github.com/adrai/flowchart.js.git#751717d3db6437def9a5f8b1cb73e8bb81b5833a",
"monetizejs": "~0.2.0",
"MathJax": "2.6.1",
"alertify.js": "https://github.com/fabien-d/alertify.js.git#fc2e06fa39873363dda199204b8544119ab060bf"
},
"main": [
"/public/res/main.js",
"/public/res/styles/jquery.jgrowl.css"
]
}

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

@ -1,35 +0,0 @@
require('./check-versions')()
process.env.NODE_ENV = 'production'
var ora = require('ora')
var rm = require('rimraf')
var path = require('path')
var chalk = require('chalk')
var webpack = require('webpack')
var config = require('../config')
var webpackConfig = require('./webpack.prod.conf')
var spinner = ora('building for production...')
spinner.start()
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, function (err, stats) {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + '\n\n')
console.log(chalk.cyan(' Build complete.\n'))
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'
))
})
})

View File

@ -1,48 +0,0 @@
var chalk = require('chalk')
var semver = require('semver')
var packageConfig = require('../package.json')
var shell = require('shelljs')
function exec (cmd) {
return require('child_process').execSync(cmd).toString().trim()
}
var versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
},
]
if (shell.which('npm')) {
versionRequirements.push({
name: 'npm',
currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm
})
}
module.exports = function () {
var warnings = []
for (var i = 0; i < versionRequirements.length; i++) {
var mod = versionRequirements[i]
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + ': ' +
chalk.red(mod.currentVersion) + ' should be ' +
chalk.green(mod.versionRequirement)
)
}
}
if (warnings.length) {
console.log('')
console.log(chalk.yellow('To use this template, you must update following to modules:'))
console.log()
for (var i = 0; i < warnings.length; i++) {
var warning = warnings[i]
console.log(' ' + warning)
}
console.log()
process.exit(1)
}
}

View File

@ -1,24 +0,0 @@
#!/bin/bash
set -e
# Tag and push docker image
docker login -u benweet -p "$DOCKER_PASSWORD"
docker tag benweet/stackedit "benweet/stackedit:$TRAVIS_TAG"
docker push benweet/stackedit:$TRAVIS_TAG
docker tag benweet/stackedit:$TRAVIS_TAG benweet/stackedit:latest
docker push benweet/stackedit:latest
# Build the chart
cd "$TRAVIS_BUILD_DIR"
npm run chart
# Add chart to helm repository
git clone --branch master "https://benweet:$GITHUB_TOKEN@github.com/benweet/stackedit-charts.git" /tmp/charts
cd /tmp/charts
helm package "$TRAVIS_BUILD_DIR/dist/stackedit"
helm repo index --url https://benweet.github.io/stackedit-charts/ .
git config user.name "Benoit Schweblin"
git config user.email "benoit.schweblin@gmail.com"
git add .
git commit -m "Added $TRAVIS_TAG"
git push origin master

View File

@ -1,9 +0,0 @@
/* eslint-disable */
require('eventsource-polyfill')
var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
hotClient.subscribe(function (event) {
if (event.action === 'reload') {
window.location.reload()
}
})

View File

@ -1,94 +0,0 @@
require('./check-versions')()
var config = require('../config')
Object.keys(config.dev.env).forEach((key) => {
if (!process.env[key]) {
process.env[key] = JSON.parse(config.dev.env[key]);
}
});
var opn = require('opn')
var path = require('path')
var express = require('express')
var webpack = require('webpack')
var proxyMiddleware = require('http-proxy-middleware')
var webpackConfig = require('./webpack.dev.conf')
// default port where dev server listens for incoming traffic
var port = process.env.PORT || config.dev.port
// automatically open browser, if not set will be false
var autoOpenBrowser = !!config.dev.autoOpenBrowser
// Define HTTP proxies to your custom API backend
// https://github.com/chimurai/http-proxy-middleware
var proxyTable = config.dev.proxyTable
var app = express()
var compiler = webpack(webpackConfig)
// StackEdit custom middlewares
require('../server')(app);
var devMiddleware = require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath,
quiet: true
})
var hotMiddleware = require('webpack-hot-middleware')(compiler, {
log: () => {}
})
// force page reload when html-webpack-plugin template changes
compiler.plugin('compilation', function (compilation) {
compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
hotMiddleware.publish({ action: 'reload' })
cb()
})
})
// proxy api requests
Object.keys(proxyTable).forEach(function (context) {
var options = proxyTable[context]
if (typeof options === 'string') {
options = { target: options }
}
app.use(proxyMiddleware(options.filter || context, options))
})
// handle fallback for HTML5 history API
app.use(require('connect-history-api-fallback')())
// serve webpack bundle output
app.use(devMiddleware)
// enable hot-reload and state-preserving
// compilation error display
app.use(hotMiddleware)
// serve pure static assets
var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
app.use(staticPath, express.static('./static'))
var uri = 'http://localhost:' + port
var _resolve
var readyPromise = new Promise(resolve => {
_resolve = resolve
})
console.log('> Starting dev server...')
devMiddleware.waitUntilValid(() => {
console.log('> Listening at ' + uri + '\n')
// when env is testing, don't need open it
if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
opn(uri)
}
_resolve()
})
var server = app.listen(port)
module.exports = {
ready: readyPromise,
close: () => {
server.close()
}
}

View File

@ -1,71 +0,0 @@
var path = require('path')
var config = require('../config')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
exports.assetsPath = function (_path) {
var assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path)
}
exports.cssLoaders = function (options) {
options = options || {}
var cssLoader = {
loader: 'css-loader',
options: {
minimize: process.env.NODE_ENV === 'production',
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
var loaders = [cssLoader]
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
// Extract CSS when that option is specified
// (which is the case during production build)
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
}
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'),
sass: generateLoaders('sass', { indentedSyntax: true }),
scss: generateLoaders('sass'),
stylus: generateLoaders('stylus'),
styl: generateLoaders('stylus')
}
}
// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
var output = []
var loaders = exports.cssLoaders(options)
for (var extension in loaders) {
var loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}

View File

@ -1,12 +0,0 @@
var utils = require('./utils')
var config = require('../config')
var isProduction = process.env.NODE_ENV === 'production'
module.exports = {
loaders: utils.cssLoaders({
sourceMap: isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap,
extract: isProduction
})
}

View File

@ -1,109 +0,0 @@
var path = require('path')
var webpack = require('webpack')
var utils = require('./utils')
var config = require('../config')
var VueLoaderPlugin = require('vue-loader/lib/plugin')
var vueLoaderConfig = require('./vue-loader.conf')
var StylelintPlugin = require('stylelint-webpack-plugin')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
entry: {
app: './src/'
},
node: {
// For mermaid
fs: 'empty' // jison generated code requires 'fs'
},
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'@': resolve('src')
}
},
module: {
rules: [
{
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter')
}
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
// We can't pass graphlibrary to babel
{
test: /\.js$/,
loader: 'string-replace-loader',
include: [
resolve('node_modules/graphlibrary')
],
options: {
search: '^\\s*(?:let|const) ',
replace: 'var ',
flags: 'gm'
}
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [
resolve('src'),
resolve('test'),
resolve('node_modules/mermaid')
],
exclude: [
resolve('node_modules/mermaid/src/diagrams/class/parser'),
resolve('node_modules/mermaid/src/diagrams/flowchart/parser'),
resolve('node_modules/mermaid/src/diagrams/gantt/parser'),
resolve('node_modules/mermaid/src/diagrams/git/parser'),
resolve('node_modules/mermaid/src/diagrams/sequence/parser')
],
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(ttf|eot|otf|woff2?)(\?.*)?$/,
loader: 'file-loader',
options: {
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
},
{
test: /\.(md|yml|html)$/,
loader: 'raw-loader'
}
]
},
plugins: [
new VueLoaderPlugin(),
new StylelintPlugin({
files: ['**/*.vue', '**/*.scss']
}),
new webpack.DefinePlugin({
VERSION: JSON.stringify(require('../package.json').version)
})
]
}

View File

@ -1,35 +0,0 @@
var utils = require('./utils')
var webpack = require('webpack')
var config = require('../config')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
// add hot-reload related code to entry chunks
Object.keys(baseWebpackConfig.entry).forEach(function (name) {
baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
})
module.exports = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
},
// cheap-module-eval-source-map is faster for development
devtool: 'source-map',
plugins: [
new webpack.DefinePlugin({
NODE_ENV: config.dev.env.NODE_ENV
}),
// https://github.com/glenjamin/webpack-hot-middleware#installation--usage
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true
}),
new FriendlyErrorsPlugin()
]
})

View File

@ -1,154 +0,0 @@
var path = require('path')
var utils = require('./utils')
var webpack = require('webpack')
var config = require('../config')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var CopyWebpackPlugin = require('copy-webpack-plugin')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
var OfflinePlugin = require('offline-plugin');
var WebpackPwaManifest = require('webpack-pwa-manifest')
var FaviconsWebpackPlugin = require('favicons-webpack-plugin')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
var env = config.build.env
var webpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true
})
},
devtool: config.build.productionSourceMap ? '#source-map' : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePlugin({
NODE_ENV: env.NODE_ENV,
GOOGLE_CLIENT_ID: env.GOOGLE_CLIENT_ID,
GITHUB_CLIENT_ID: env.GITHUB_CLIENT_ID
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
},
sourceMap: true
}),
// extract css into its own file
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css')
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin({
cssProcessorOptions: {
safe: true
}
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: config.build.index,
template: 'index.html',
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: 'dependency'
}),
// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module, count) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}),
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
chunks: ['vendor']
}),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
]),
new FaviconsWebpackPlugin({
logo: resolve('src/assets/favicon.png'),
title: 'StackEdit',
}),
new WebpackPwaManifest({
name: 'StackEdit',
description: 'Full-featured, open-source Markdown editor',
display: 'standalone',
orientation: 'any',
start_url: 'app',
background_color: '#ffffff',
crossorigin: 'use-credentials',
icons: [{
src: resolve('src/assets/favicon.png'),
sizes: [96, 128, 192, 256, 384, 512]
}]
}),
new OfflinePlugin({
ServiceWorker: {
events: true
},
AppCache: true,
excludes: ['**/.*', '**/*.map', '**/index.html', '**/static/oauth2/callback.html', '**/icons-*/*.png', '**/static/fonts/KaTeX_*'],
externals: ['/', '/app', '/oauth2/callback']
}),
]
})
if (config.build.productionGzip) {
var CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
if (config.build.bundleAnalyzerReport) {
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig

View File

@ -1,56 +0,0 @@
var path = require('path')
var utils = require('./utils')
var webpack = require('webpack')
var utils = require('./utils')
var config = require('../config')
var vueLoaderConfig = require('./vue-loader.conf')
var StylelintPlugin = require('stylelint-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
entry: {
style: './src/styles/'
},
module: {
rules: [{
test: /\.(ttf|eot|otf|woff2?)(\?.*)?$/,
loader: 'file-loader',
options: {
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}]
.concat(utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true
})),
},
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: config.build.assetsPublicPath
},
plugins: [
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
},
sourceMap: true
}),
// extract css into its own file
new ExtractTextPlugin({
filename: '[name].css',
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin({
cssProcessorOptions: {
safe: true
}
}),
]
}

View File

@ -1,22 +0,0 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

View File

@ -1,5 +0,0 @@
apiVersion: v1
appVersion: vSTACKEDIT_VERSION
description: In-browser Markdown editor
name: stackedit
version: STACKEDIT_VERSION

View File

@ -1,21 +0,0 @@
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
{{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }}
{{- end }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "stackedit.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "stackedit.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "stackedit.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "stackedit.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl port-forward $POD_NAME 8080:80
{{- end }}

View File

@ -1,45 +0,0 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "stackedit.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "stackedit.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "stackedit.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Common labels
*/}}
{{- define "stackedit.labels" -}}
app.kubernetes.io/name: {{ include "stackedit.name" . }}
helm.sh/chart: {{ include "stackedit.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}

View File

@ -1,87 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "stackedit.fullname" . }}
labels:
{{ include "stackedit.labels" . | indent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "stackedit.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "stackedit.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
volumeMounts:
- mountPath: /run
name: run-volume
- mountPath: /tmp
name: tmp-volume
env:
- name: PORT
value: "80"
- name: PAYPAL_RECEIVER_EMAIL
value: {{ .Values.paypalReceiverEmail }}
- name: AWS_ACCESS_KEY_ID
value: {{ .Values.awsAccessKeyId }}
- name: AWS_SECRET_ACCESS_KEY
value: {{ .Values.awsSecretAccessKey }}
- name: DROPBOX_APP_KEY
value: {{ .Values.dropboxAppKey }}
- name: DROPBOX_APP_KEY_FULL
value: {{ .Values.dropboxAppKeyFull }}
- name: GOOGLE_CLIENT_ID
value: {{ .Values.googleClientId }}
- name: GOOGLE_API_KEY
value: {{ .Values.googleApiKey }}
- name: GITHUB_CLIENT_ID
value: {{ .Values.githubClientId }}
- name: GITHUB_CLIENT_SECRET
value: {{ .Values.githubClientSecret }}
- name: WORDPRESS_CLIENT_ID
value: {{ .Values.wordpressClientId }}
- name: WORDPRESS_SECRET
value: {{ .Values.wordpressSecret }}
ports:
- name: http
containerPort: 80
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumes:
- name: run-volume
emptyDir: {}
- name: tmp-volume
emptyDir: {}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@ -1,39 +0,0 @@
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "stackedit.fullname" . -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
{{ include "stackedit.labels" . | indent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ . }}
pathType: Prefix
backend:
service:
name: {{ $fullName }}
port:
name: http
{{- end }}
{{- end }}
{{- end }}

View File

@ -1,16 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "stackedit.fullname" . }}
labels:
{{ include "stackedit.labels" . | indent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
app.kubernetes.io/name: {{ include "stackedit.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}

View File

@ -1,15 +0,0 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "stackedit.fullname" . }}-test-connection"
labels:
{{ include "stackedit.labels" . | indent 4 }}
annotations:
"helm.sh/hook": test-success
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "stackedit.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never

View File

@ -1,71 +0,0 @@
# Default values for stackedit.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
dropboxAppKey: ""
dropboxAppKeyFull: ""
googleClientId: ""
googleApiKey: ""
githubClientId: ""
githubClientSecret: ""
giteeClientId: ""
giteeClientSecret: ""
wordpressClientId: ""
wordpressSecret: ""
paypalReceiverEmail: ""
awsAccessKeyId: ""
awsSecretAccessKey: ""
giteaClientId: ""
giteaClientSecret: ""
giteaUrl: ""
gitlabClientId: ""
gitlabUrl: ""
replicaCount: 1
image:
repository: benweet/stackedit
tag: vSTACKEDIT_VERSION
pullPolicy: IfNotPresent
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
service:
type: ClusterIP
port: 80
ingress:
enabled: false
annotations:
# kubernetes.io/ingress.class: nginx
# certmanager.k8s.io/issuer: letsencrypt-prod
# certmanager.k8s.io/acme-challenge-type: http01
hosts: []
# - host: stackedit.example.com
# paths:
# - /
tls: []
# - secretName: stackedit-tls
# hosts:
# - stackedit.example.com
resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
nodeSelector: {}
tolerations: []
affinity: {}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

BIN
chrome-app/logo-128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -1,28 +1,23 @@
{
"name": "StackEdit中文版",
"description": "支持Gitee仓库/粘贴图片自动上传的浏览器内 Markdown 编辑器",
"version": "5.15.17",
"manifest_version": 2,
"container": "GITEE",
"api_console_project_id": "241271498917",
"icons": {
"16": "icon-16.png",
"32": "icon-32.png",
"64": "icon-64.png",
"128": "icon-128.png",
"256": "icon-256.png",
"512": "icon-512.png"
},
"app": {
"urls": [
"https://md.jonylee.top/"
],
"launch": {
"web_url": "https://md.jonylee.top/app"
}
},
"offline_enabled": true,
"permissions": [
"unlimitedStorage"
]
"name": "StackEdit",
"description": "In-browser markdown editor",
"version": "1.0.10",
"manifest_version": 2,
"container" : "GOOGLE_DRIVE",
"api_console_project_id" : "241271498917",
"icons": {
"128": "logo-128.png"
},
"app": {
"urls": [
"https://stackedit.io/"
],
"launch": {
"web_url": "https://stackedit.io/editor"
}
},
"offline_enabled": true,
"permissions": [
"unlimitedStorage"
]
}

View File

@ -1,18 +0,0 @@
var merge = require('webpack-merge')
var prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
// 以下配置是开发临时用的配置 随时可能失效 请替换为自己的
GITHUB_CLIENT_ID: '"845b8f75df48f2ee0563"',
GITHUB_CLIENT_SECRET: '"80df676597abded1450926861965cc3f9bead6a0"',
GITEE_CLIENT_ID: '"925ba7c78b85dec984f7877e4aca5cab10ae333c6d68e761bdb0b9dfb8f55672"',
GITEE_CLIENT_SECRET: '"f05731066e42d307339dc8ebbb037a103881dafc7207a359a393b87749f1c562"',
CLIENT_ID: '"thF3qCGLN39OtafjGnqHyj6n02WwE6xD"',
// GITEA_CLIENT_ID: '"fe30f8f9-b1e8-4531-8f72-c1a5d3912805"',
// GITEA_CLIENT_SECRET: '"lus7oMnb3H6M1hsChndphArE20Txr7erwJLf7SDBQWTw"',
// GITEA_URL: '"https://gitea.test.com"',
GITLAB_CLIENT_ID: '"074cd5103c62dea0f479dac861039656ac80935e304c8113a02cc64c629496ae"',
GITLAB_CLIENT_SECRET: '"6f406f24216b686d55d28313dec1913c2a8e599afdb08380d5e8ce838e16e41e"',
GITLAB_URL: '"http://gitlab.qicoder.com"',
})

View File

@ -1,39 +0,0 @@
// see http://vuejs-templates.github.io/webpack for documentation.
var path = require('path')
module.exports = {
build: {
env: require('./prod.env'),
index: path.resolve(__dirname, '../dist/index.html'),
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: '/',
productionSourceMap: true,
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: false,
productionGzipExtensions: ['js', 'css'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
},
dev: {
env: require('./dev.env'),
port: 80,
autoOpenBrowser: false,
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {},
// CSS Sourcemaps off by default because relative paths are "buggy"
// with this option, according to the CSS-Loader README
// (https://github.com/webpack/css-loader#sourcemaps)
// In our experience, they generally work as expected,
// just be aware of this issue when enabling this option.
// cssSourceMap: false
cssSourceMap: true
}
}

View File

@ -1,3 +0,0 @@
module.exports = {
NODE_ENV: '"production"'
}

180
couchdb/setup.js Normal file
View File

@ -0,0 +1,180 @@
var validate = function(newDoc) {
Object.keys(newDoc).forEach(function(key) {
if(key[0] !== '_' && [
'updated',
'tags',
'title'
].indexOf(key) === -1) {
throw({forbidden: 'Unknown document attribute: ' + key});
}
});
var toString = Object.prototype.toString;
if(toString.call(newDoc._id) !== '[object String]') {
throw({forbidden: 'ID must be a string.'});
}
if(!newDoc._id.match(/[a-zA-Z0-9]{24}/)) {
throw({forbidden: 'Invalid ID format.'});
}
if(newDoc._deleted) {
if(newDoc.updated !== undefined ||
newDoc.tags !== undefined ||
newDoc.title !== undefined ||
newDoc._attachments !== undefined) {
throw({forbidden: 'Deleted document must be empty.'});
}
return;
}
if(toString.call(newDoc.updated) !== '[object Number]') {
throw({forbidden: 'Update time must be an integer.'});
}
if(newDoc.updated > Date.now() + 300000) {
throw({forbidden: 'Update time is in the future, please check your clock!'});
}
if(toString.call(newDoc.title) !== '[object String]') {
throw({forbidden: 'Title must be a string.'});
}
if(!newDoc.title) {
throw({forbidden: 'Title is empty.'});
}
if(newDoc.title.length >= 256) {
throw({forbidden: 'Title too long.'});
}
if(newDoc.tags !== undefined) {
if(toString.call(newDoc.tags) !== '[object Array]') {
throw({forbidden: 'Tags must be an array.'});
}
if(newDoc.tags.length >= 16) {
throw({forbidden: 'Too many tags.'});
}
newDoc.tags.forEach(function(tag) {
if(toString.call(tag) !== '[object String]') {
throw({forbidden: 'Tags must contain strings only.'});
}
if(!tag) {
throw({forbidden: 'Tag is empty.'});
}
if(tag.length > 32) {
throw({forbidden: 'Tag is too long.'});
}
});
}
var attachment = (newDoc._attachments || {}).content;
if(!attachment) {
throw({forbidden: 'Missing attached content.'});
}
if(attachment.content_type != 'text/plain') {
throw({forbidden: 'Invalid content type.'});
}
if(Object.keys(newDoc._attachments).length > 1) {
throw({forbidden: 'Too many attachments.'});
}
};
var byUpdate = function(doc) {
if(!doc.tags || !doc.tags.length) {
emit(doc.updated, null);
}
};
var byTagAndUpdate = function(doc) {
doc.tags && doc.tags.forEach(function(tag) {
emit([
tag,
doc.updated
], null);
});
};
var ddocs = [
{
path: '/_design/validate',
body: {
validate_doc_update: validate.toString()
}
},
{
path: '/_design/by_update',
body: {
views: {
default: {
map: byUpdate.toString()
}
}
}
},
{
path: '/_design/by_tag_and_update',
body: {
views: {
default: {
map: byTagAndUpdate.toString()
}
}
}
}
];
if(process.argv.length < 3) {
console.error('Missing URL parameter');
process.exit(-1);
}
var url = require('url').parse(process.argv[2]);
var request = require(url.protocol === 'https:' ? 'https' : 'http').request;
function onError(err, body) {
console.error(err);
body && console.error(body);
process.exit(1);
}
function uploadDdoc() {
if(ddocs.length === 0) {
return console.log('All design documents updated successfully.');
}
var ddoc = ddocs.shift();
var options = {
hostname: url.hostname,
port: url.port,
path: url.path + ddoc.path,
auth: url.auth,
method: 'GET'
};
request(options, function(res) {
var body = '';
res
.on('data', function(chunk) {
body += chunk;
})
.on('end', function() {
if(res.statusCode == 200) {
ddoc.body._rev = JSON.parse(body)._rev;
}
var options = {
hostname: url.hostname,
port: url.port,
path: url.path + ddoc.path,
auth: url.auth,
method: 'PUT',
headers: {
'Content-type': 'application/json'
}
};
request(options, function(res) {
var body = '';
res
.on('data', function(chunk) {
body += chunk;
})
.on('end', function() {
res.statusCode >= 300 && onError('Status code: ' + res.statusCode, body);
uploadDdoc();
});
})
.on('error', onError)
.end(JSON.stringify(ddoc.body));
});
})
.on('error', onError)
.end();
}
uploadDdoc();

52
doc/couchdb-setup.md Normal file
View File

@ -0,0 +1,52 @@
### Pre-requisites
- **CouchDB 1.5 or later**, because of the use of `POST /{db}/_changes`,
- **Node.js**, to load the design documents in the database.
> **Note:**
>
> - In order to work with https://stackedit.io, your database has to be accessible through HTTPS. You can use a free hosting service like [Smileupps](https://www.smileupps.com/) or [configure your own instance to use SSL](http://docs.couchdb.org/en/latest/config/http.html#ssl).
>
> - StackEdit doesn't deal with user access rights, but you can still set permissions for your database and configure StackEdit to connect to it using URL like this: `https://username:password@instance.smileupps.com/documents`.
>
> - It's up to you to trigger the database compaction, or to keep the full history of your documents.
### Enable CORS
Add the following key/value pairs to your CouchDB configuration:
```
[httpd]
enable_cors = true
[cors]
origins = http://localhost, https://stackedit.io
```
### Create the database
```bash
curl -X PUT https://instance.smileupps.com/documents
```
### Insert the design documents
```bash
curl -O https://raw.githubusercontent.com/benweet/stackedit/master/couchdb/setup.js
node setup.js https://instance.smileupps.com/documents
```
Or directly:
```bash
curl https://raw.githubusercontent.com/benweet/stackedit/master/couchdb/setup.js | node /dev/stdin https://instance.smileupps.com/documents
```
### Update StackEdit settings
To configure StackEdit to use your CouchDB instance, change the in URL in `Menu` > `Settings` > `Advanced` > `CouchDB URL` to `https://instance.smileupps.com/documents`.
> Written with [StackEdit](https://stackedit.io/).

659
doc/developer-guide.md Normal file
View File

@ -0,0 +1,659 @@
Developer guide
===============
Getting started
---------------
### Pre-requisites
- [Git][1]
- [node.js/npm][2]
- [Gulp][3]
- [Bower][4]
### Before debugging
- Download development tools:
npm install
- Download dependencies:
bower install
- Serve **StackEdit** at `http://localhost/`:
(export PORT=80 && node server.js)
If on Windows, use
(set PORT=80 && node server.js)
- Run **StackEdit** in debug mode (no application cache, serve original files instead of minified):
http://localhost/?debug
### Add new dependencies
> **NOTE:** StackEdit uses [RequireJS][5] for asynchronous module definition ([AMD][6]).
- Install new dependencies using [Bower][7]:
bower install <library> --save
- Add the new dependency to [RequireJS][8] configuration file (`main.js`):
gulp bower-requirejs
### Build/minify
gulp
### Deploy
- on Heroku:
heroku create my-stackedit-instance
git push heroku master
- in a Docker container:
docker build -t my-stackedit-image .
docker run -p 3000 my-stackedit-image
> **NOTE:** OAuth authorizations work out of the box for address `http://localhost/` except for WordPress. To allow another address, you have to add specific keys at the end of `constants.js` and eventually to set up specific proxies with the corresponding key/secret pairs ([WordPress Proxy][9], [Tumblr Proxy][10] and [Gatekeeper][11]).
Architecture
------------
![Architecture diagram][12]
The modules are loaded by RequireJS in the following order:
1. The 3rd party libraries (jQuery, underscore.js...)
2. The `Extension` modules
3. The `eventMgr` module
4. The `core` module
5. The `fileMgr` module and the helpers modules
6. The `Provider` modules
7. The `publisher` and `synchronizer` modules
This is important to notice in order to avoid circular dependencies. For instance, if an `Extension` is declared with the `core` module as a dependency, RequireJS will inject `undefined` instead of the actual module.
Any module though can access any dependencies by implementing the proper [injection listener][13] provided by the `eventMgr`.
----------
### core
The `core` module is responsible for:
- creating the [UI Layout][14], the [ACE][15] editor and the [PageDown][16] editor,
- loading/saving the settings,
- running periodic tasks,
- detecting the user activity,
- checking the offline status.
**Attributes:**
- `isOffline`: indicates the offline status of the application.
**Methods:**
- `onReady(callback)`: sets a callback to be called when all modules have been loaded and the DOM is ready.
> **NOTE:** This is preferred over [jQuery's `.ready()`][17] because it ensures that all AMD modules are loaded by [RequireJS][18].
- `runPeriodically(callback)`: sets a callback to be called every second.
> **NOTE:** The callback will not run if the user is inactive or in StackEdit Viewer. User is considered inactive after 5 minutes of inactivity (mouse or keyboard).
- `setOffline()`: can be called by any other modules when a network timeout occurs for instance.
> **NOTE:** the offline status is also set by detecting the window `offline` event. `core.isOffline` is automatically set to `false` when the network is recovered.
- `initEditor(fileDesc)`: creates or refreshes the [PageDown][19] editor with a given [`FileDescriptor`][20] object.
----------
### fileMgr
The `fileMgr` module is responsible for:
- creating and deleting local files,
- switching from one file to another.
**Attributes:**
- `currentFile`: the [`FileDescriptor`][21] object that is currently edited.
**Methods:**
- `createFile(title, content)`: creates a [`FileDescriptor`][22] object, add it in the [`fileSystem`][23] map and returns it.
- `deleteFile(fileDesc)`: deletes a [`FileDescriptor`][24] object from the [`fileSystem`][25] map.
- `selectFile(fileDesc)`: selects a [`FileDescriptor`][26] object for editing.
#### FileDescriptor
The `FileDescriptor` class represents a local file. A `FileDescriptor` object has the following properties:
- `fileIndex`: the unique string index of the file in the file system.
- `title`: the title of the document.
- `content`: the content of the document.
- `syncLocations`: a map containing all the associated [`syncAttributes`][27] objects with their `syncIndex` as a key.
- `publishLocations`: a map containing all the associated [`publishAttributes`][28] objects with their `publishIndex` as a key.
And the following methods:
- `addSyncLocation(syncAttributes)`: associates a [`syncAttributes`][29] object with the file.
- `removeSyncLocation(syncAttributes)`: unassociates a [`syncAttributes`][30] object with the file.
- `addPublishLocation(publishAttributes)`: associates a [`publishAttributes`][31] object with the file.
- `removePublishLocation(publishAttributes)`: unassociates a [`publishAttributes`][32] object with the file.
#### fileSystem
The `fileSystem` module is a map containing all the [`FileDescriptor`][33] objects with their `fileIndex` as a key.
----------
### synchronizer
The `synchronizer` module is responsible for:
- creating a new local file from a sync location (import).
- creating a new sync location from a local file (export).
- running 2 ways synchronization (upload and download) for all sync locations.
#### synchronizer's providers
A [`provider`][34] module can be associated with the `synchronizer` module if it implements the following functions:
- `importFiles()`: downloads one or multiple files and create local files associated with the sync locations.
- `exportFile()`: uploads a local file to a new sync location.
- `syncDown()`: performs a download of all the changes operated on all sync locations.
- `syncUp()`: performs an upload of a change to a sync location.
#### syncAttributes
A `syncAttributes` object is an object that describes a sync location. Attributes differ from one provider to another except for the following:
- `syncIndex`: the unique string index of the publish location.
- `provider`: the [`provider`][35] module that handles the sync location.
----------
### publisher
The `publisher` module is responsible for:
- creating new publish locations,
- updating existing publish locations.
#### publisher's providers
A [`provider`][36] module can be associated with the `publisher` module if it implements the following functions:
- `newPublishAttributes()`: returns a new [`publishAttributes`][37] object in order to create a new publish location.
- `publish()`: performs publishing of one publish location.
#### publishAttributes
A `publishAttributes` object is an object that describes a publish location. Attributes differ from one provider to another except for the following:
- `publishIndex`: the unique string index of the publish location.
- `provider`: the [`provider`][38] module that handles the publish location.
- `format`: the publishing format for the publish location. It can be:
- `markdown` for Markdown format.
- `html` for HTML format.
- `template` for template format.
----------
### eventMgr
The `eventMgr` module is responsible for receiving and dispatching events. Below is the list of all events signatures.
Most events (those that are not triggered by the `eventMgr` module) can be triggered by calling methods of the same name in the `eventMgr` module. For example:
```js
eventMgr.onMessage('StackEdit is awesome!');
```
The method `addListener(eventName, callback)` of the `eventMgr` module can be used to listen to these events (except those that can only be handled by `Extension` modules). For example:
```js
eventMgr.addListener('onMessage', function(message) {
alert(message);
});
```
`Extension` modules have the possibility to listen to those events by implementing methods of the same name. For example:
```js
myExtension.onMessage = function(message) {
alert(message);
};
```
----------
#### Core events
- **`onReady()`**
All the modules are loaded and the DOM is ready.
> Triggered by the `core` module.
> This is preferred over [jQuery's `.ready()`][39] because it ensures that all modules have been loaded by RequireJS.
- **`onMessage(message)`**
A message destined to the user has been produced.
- `message`: the text string of the message.
- **`onError(error)`**
An error has been thrown.
- `error`: an error object or a string.
- **`onOfflineChanged(isOffline)`**
The off-line status has changed.
- `isOffline`: the off-line status.
> Triggered by the `core` module.
- **`onUserActive()`**
The user has just moved the mouse or pressed the keyboard.
> Triggered by the `core` module.
- **`onAsyncRunning(isRunning)`**
Some asynchronous tasks have just started or stopped.
- `isRunning`: true if started, false if stopped.
> Triggered by the `AsyncTask` module.
- **`onPeriodicRun()`**
A hook that is called periodically (every 1 second if user is active).
> Triggered by the `core` module.
- **`onLoadSettings()`**
A hook that is called when the settings dialog has to be refreshed. Every `Extension` module that has configuration inputs in the settings dialog has to implement a listener for this event.
> Triggered by the `core` module. Only `Extension` modules can handle this event.
- **`onSaveSettings(newConfig, event)`**
A hook that is called when the settings dialog has to be validated. Every `Extension` module that has configuration inputs in the settings dialog has to implement a listener for this event.
- `newConfig`: the new configuration object, deduced from the settings dialog inputs.
- `event`: the submit event object. `stopPropagation` has to be called in case of an error when parsing settings dialog inputs.
> Triggered by the `core` module. Only `Extension` modules can handle this event.
- **`onInit()`**
A hook allowing enabled extensions to initialize.
> Triggered by the `eventMgr` module. Only `Extension` modules can handle this event.
> This event is triggered before `onReady` event and just after the `config` and `enabled` extensions properties have been set by the `eventMgr`.
----------
#### Module injection events
- **`onFileMgrCreated(fileMgr)`**
The `fileMgr` module has been created.
- `fileMgr`: the `fileMgr` module.
> Triggered by the `fileMgr` module.
- **`onSynchronizerCreated(synchronizer)`**
The `synchronizer` module has been created.
- `synchronizer`: the `synchronizer` module.
> Triggered by the `synchronizer` module.
- **`onPublisherCreated(publisher)`**
The `publisher` module has been created.
- `publisher`: the `publisher` module.
> Triggered by the `publisher` module.
- **`onEventMgrCreated()`**
The `eventMgr` module has been created.
- `eventMgr`: the `eventMgr` module.
> Triggered by the `eventMgr` module.
----------
#### file operation events
- **`onFileCreated(fileDesc)`**
A [`FileDescriptor`][41] object has been created.
- `fileDesc`: the [`FileDescriptor`][42] object.
> Triggered by the `fileMgr` module.
- **`onFileDeleted(fileDesc)`**
A [`FileDescriptor`][43] object has been removed from the `fileSystem` module.
- `fileDesc`: the [`FileDescriptor`][44] object.
> Triggered by the `fileMgr` module.
- **`onFileSelected(fileDesc)`**
A [`FileDescriptor`][45] object has been selected.
- `fileDesc`: the [`FileDescriptor`][46] object.
> Triggered by the `fileMgr` module. This event is triggered before `onFileClosed` (if another document is open) and `onFileOpen` events.
- **`onFileClosed(fileDesc)`**
The current [`FileDescriptor`][47] object is about to be detached from the editor.
- `fileDesc`: the [`FileDescriptor`][48] object.
> Triggered by the `fileMgr` module. This event is triggered after `onFileSelected` event and before `onFileClosed` event.
- **`onFileOpen(fileDesc)`**
The selected [`FileDescriptor`][49] object has been attached to the editor.
- `fileDesc`: the [`FileDescriptor`][50] object.
> Triggered by the `fileMgr` module. This event is triggered after `onFileSelected` and `onFileClosed` (if another document is open) events.
- **`onContentChanged(fileDesc)`**
The content of a [`FileDescriptor`][51] object has been modified.
- `fileDesc`: the [`FileDescriptor`][52] object.
- **`onTitleChanged(fileDesc)`**
The content of a [`FileDescriptor`][53] object has been modified.
- `fileDesc`: the [`FileDescriptor`][54] object.
- **`onFoldersChanged()`**
The folders structure has changed.
----------
#### Sync events
- **`onSyncRunning(isRunning)`**
A synchronization job has just started or stopped.
- `isRunning`: true if started, false if stopped.
> Triggered by the `synchronizer` module.
> A synchronization job is the action to download and upload all detected changes for all sync locations of all documents.
- **`onSyncSuccess()`**
A synchronization job has successfully finished.
> Triggered by the `synchronizer` module.
> A synchronization job is the action to download and upload all detected changes for all sync locations of all documents.
- **`onSyncImportSuccess(fileDescList, provider)`**
The import of documents has successfully finished.
- `fileDescList`: the list of [`FileDescriptor`][55] objects that have been created.
- `provider`: the [`provider`][56] module that handled the import.
> Triggered by the [`provider`][57] module that handled the import.
> An import is the action to download multiple files and to create, for each, one [`FileDescriptor`][55] objects with one sync location.
- **`onSyncExportSuccess(fileDesc, syncAttributes)`**
The export of one document has successfully finished.
- `fileDesc`: the [`FileDescriptor`][58] object that has been exported.
- `syncAttributes`: the descriptor object of the new sync location.
> Triggered by the `synchronizer` module.
> An export is the action to upload one file and to create one new sync location associated with one existing `FileDescriptor`][55] object.
- **`onSyncRemoved(fileDesc, syncAttributes)`**
A sync location has been removed from a [`FileDescriptor`][59] object.
- `fileDesc`: the [`FileDescriptor`][60] object.
- `syncAttributes`: the descriptor object of the removed sync location.
----------
#### Publish events
- **`onPublishRunning(isRunning)`**
A document publication job has just started or stopped.
- `isRunning`: true if started, false if stopped.
> Triggered by the `publisher` module.
> A publication job is the action to upload changes on multiple publish locations associated with one `FileDescriptor`][55] object.
- **`onPublishSuccess(fileDesc)`**
A document publication job has successfully finished.
- `fileDesc`: the [`FileDescriptor`][60] object that has been published.
> Triggered by the `publisher` module.
> A publication job is the action to upload changes on multiple publish locations associated with one `FileDescriptor`][55] object.
- **`onNewPublishSuccess(fileDesc, publishAttributes)`**
A new publish location has been successfully created.
- `fileDesc`: the [`FileDescriptor`][60] object that has been published.
- `publishAttributes`: the descriptor object of the new publish location.
> Triggered by the `publisher` module.
- **`onPublishRemoved(fileDesc, publishAttributes)`**
A publish location has been removed from a [`FileDescriptor`][59] object.
- `fileDesc`: the [`FileDescriptor`][60] object.
- `publishAttributes`: the descriptor object of the removed publish location.
> Triggered by the `publisher` module.
----------
#### UI Layout events
- **`onLayoutConfigure(layoutConfig)`**
The layout is about to be configured.
- `layoutConfig`: the configuration object of the UI Layout library.
> Triggered by the `core` module.
- **`onLayoutCreated(layout)`**
The layout has just been created.
- `layout`: the layout object of the UI Layout library.
> Triggered by the `core` module.
- **`onLayoutResize(paneName)`**
One pane of the layout has been resized.
- `paneName`: the name of the resized layout pane.
> Triggered by the `core` module.
- **`onCreateButton()`**
Allows extensions to add their own buttons in the navigation bar. Implemented listeners have to return an HTML button element. For example:
myExtension.onCreateButton = function() {
var button = $('<button class="btn btn-success"><i class="icon-rocket"></i></button>');
button.click(function() {
eventMgr.onMessage('Booom!');
});
return button[0];
};
> Triggered by the `eventMgr` module. Only `Extension` modules can handle this event.
- **`onCreateEditorButton()`**
Allows extensions to add their own buttons in the side bar. Implemented listeners have to return an HTML button element. See `onCreateButton` for a concrete example.
> Triggered by the `eventMgr` module. Only `Extension` modules can handle this event.
- **`onCreatePreviewButton()`**
Allows extensions to add their own buttons over the preview. Implemented listeners have to return an HTML button element. See `onCreateButton` for a concrete example.
> Triggered by the `eventMgr` module. Only `Extension` modules can handle this event.
----------
#### PageDown events
- **`onPagedownConfigure(editor)`**
The Pagedown editor is about to be created.
- `editor`: the Pagedown editor object before `run` has been called.
> Triggered by the `core` module.
- **`onAsyncPreview(callback)`**
Called after Pagedown's synchronous rendering to trigger extra asynchronous rendering (such as MathJax). Implemented listeners have to call the callback parameter after processing in order other `onAsyncPreview` listeners to run.
- `callback`: the callback to call at the end of the asynchronous processing.
> Triggered by the `eventMgr` module. Only `Extension` modules can handle this event.
- **`onPreviewFinished(html)`**
Called after every `onAsyncPreview` listeners have been called.
- `html`: the finally rendered HTML.
- **`onSectionsCreated(sectionList)`**
The Markdown has been split into sections before rendering.
- `sectionList`: the list of section objects. Each section object contains:
- `text`: the markdown substring contained in the section.
- `textWithDelimiter`: the text with an added delimiter.
> Triggered by the `markdownSectionParser` extension.
- **`onMarkdownTrim(offset)`**
The Markdown has been left trimmed by a certain number of character.
- `offset`: the number of characters that have been removed.
> Triggered by the `yamlFrontMatterParser` extension.
----------
#### ACE events
- **`onAceCreated(aceEditor)`**
The ACE editor has just been created.
- `aceEditor`: the ACE editor object.
> Triggered by the `core` module.
> Written with [StackEdit](https://stackedit.io/).
[1]: http://git-scm.com/
[2]: http://nodejs.org/
[3]: http://gulpjs.com/
[4]: http://bower.io/
[5]: http://requirejs.org/ "RequireJS"
[6]: http://en.wikipedia.org/wiki/Asynchronous_module_definition "Asynchronous module definition"
[7]: http://bower.io/
[8]: http://requirejs.org/ "RequireJS"
[9]: https://github.com/benweet/stackedit-wordpress-proxy
[10]: https://github.com/benweet/stackedit-tumblr-proxy
[11]: https://github.com/prose/gatekeeper
[12]: https://lh6.googleusercontent.com/-sr6zRtyaoUk/Un5qSakOzPI/AAAAAAAAFC0/oI5If5fI9Gw/s0/StackEdit%252520architecture%252520-%252520New%252520Page%252520%2525283%252529.png "StackEdit architecture"
[13]: #module-injection
[14]: http://layout.jquery-dev.net/ "UI Layout"
[15]: http://ace.c9.io
[16]: https://code.google.com/p/pagedown/ "PageDown"
[17]: http://api.jquery.com/ready/
[18]: http://requirejs.org/ "RequireJS"
[19]: https://code.google.com/p/pagedown/ "PageDown"
[20]: #filedescriptor
[21]: #filedescriptor
[22]: #filedescriptor
[23]: #filesystem
[24]: #filedescriptor
[25]: #filesystem
[26]: #filedescriptor
[27]: #syncattributes
[28]: #publishattributes
[29]: #syncattributes
[30]: #syncattributes
[31]: #publishattributes
[32]: #publishattributes
[33]: #filedescriptor
[34]: #provider
[35]: #provider
[36]: #provider
[37]: #publishattributes
[38]: #provider
[39]: http://api.jquery.com/ready/
[40]: http://requirejs.org/ "RequireJS"
[41]: #filedescriptor
[42]: #filedescriptor
[43]: #filedescriptor
[44]: #filedescriptor
[45]: #filedescriptor
[46]: #filedescriptor
[47]: #filedescriptor
[48]: #filedescriptor
[49]: #filedescriptor
[50]: #filedescriptor
[51]: #filedescriptor
[52]: #filedescriptor
[53]: #filedescriptor
[54]: #filedescriptor
[55]: #filedescriptor
[56]: #provider
[57]: #provider
[58]: #filedescriptor
[59]: #filedescriptor
[60]: #filedescriptor

BIN
doc/img/architecture.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

34
doc/theming.md Normal file
View File

@ -0,0 +1,34 @@
StackEdit theming guide
=======================
In **StackEdit**, a theme is pretty much a [LESS][1] file that overrides the default look and feel.
### Create your special theme very quickly by following these steps
1. Fork **StackEdit** on [GitHub][2] and clone the repository localy.
2. Install the development tools as described in the [Developer guide][3].
3. In `res/themes`, create a LESS file, just like the other themes.
> You can put images in `res/img`.
4. Add an entry in `THEME_LIST` at the end of `public/res/constants.js` with the filename as a key and the name of your theme as a value.
> **Example:** `"cool": "The coolest ever"`
5. Run the application on your machine using the `?debug` flag. Basically:
http://localhost/stackedit/?debug
6. Go to `Settings -> Editor -> Theme` and select your theme. Check that everything is fine.
7. Commit, push, create a pull request and wait for publishing.
> Written with [StackEdit](http://benweet.github.io/stackedit/).
[1]: http://lesscss.org/
[2]: https://github.com/benweet/stackedit
[3]: https://github.com/benweet/stackedit/blob/master/doc/developer-guide.md#getting-started

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` 查看帮助文档。

View File

@ -1,20 +0,0 @@
# GitHub应用配置说明
> StackEdit中文版部署如果需要支持GitHub则需要到GitHub创建一个应用并复制其中的clientId和clientSecret填充到环境变量 GITHUB_CLIENT_ID 和 GITHUB_CLIENT_SECRET 中。
# 如何创建GitHub应用
按下面图的指示创建
![](../images/github/github01.png)
![](../images/github/github02.png)
![](../images/github/github03.png)
![](../images/github/github04.png)
![](../images/github/github05.png)

View File

@ -1,35 +0,0 @@
# Gitea应用配置说明
> StackEdit中文版支持Gitea则需要到Gitea创建一个应用在StackEdit中文版绑定Gitea账号的时候填入。
# 如何创建Gitea应用
按下面图的指示创建
![](../images/gitea/gitea01.png)
![](../images/gitea/gitea02.png)
![](../images/gitea/gitea03.png)
![](../images/gitea/gitea04.png)
创建成功后即可看到应用ID 和 应用秘钥。
# Gitea跨域问题
由于StackEdit中文版是从浏览器直接访问Gitea接口故个人部署的Gitea需要支持跨域至于如何支持跨域请参考官方文档https://docs.gitea.io/en-us/config-cheat-sheet/#cors-cors (官方跨域的支持好像存在问题我个人包括很多网友通过这个配置支持跨域都失败了如果你也失败了可以试试用nginx代理实现跨域)
nginx配置实现跨域的配置如下
```
add_header Access-Control-Allow-Headers *;
add_header Access-Control-Allow-Origin $http_origin;
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
if ($request_method = 'OPTIONS') {
return 204;
}
```

View File

@ -1,19 +0,0 @@
# Gitee应用配置说明
> StackEdit中文版部署如果需要支持Gitee则需要到Gitee创建一个应用并复制其中的clientId和clientSecret填充到环境变量 GITEE_CLIENT_ID 和 GITEE_CLIENT_SECRET 中。
# 如何创建Gitee应用
按下面图的指示创建
![](../images/gitee/gitee01.png)
![](../images/gitee/gitee02.png)
![](../images/gitee/gitee03.png)
![](../images/gitee/gitee04.png)
创建成功后即可看到client id 和 client secret。

View File

@ -1,20 +0,0 @@
const path = require('path');
const gulp = require('gulp');
const concat = require('gulp-concat');
const prismScripts = [
'prismjs/components/prism-core',
'prismjs/components/prism-markup',
'prismjs/components/prism-clike',
'prismjs/components/prism-c',
'prismjs/components/prism-javascript',
'prismjs/components/prism-css',
'prismjs/components/prism-ruby',
'prismjs/components/prism-cpp',
].map(require.resolve);
prismScripts.push(
path.join(path.dirname(require.resolve('prismjs/components/prism-core')), 'prism-!(*.min).js'));
gulp.task('build-prism', () => gulp.src(prismScripts)
.pipe(concat('prism.js'))
.pipe(gulp.dest(path.dirname(require.resolve('prismjs')))));

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 793 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 339 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 726 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 195 KiB

View File

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

View File

@ -1,27 +0,0 @@
const env = require('./config/prod.env');
Object.keys(env).forEach((key) => {
if (!process.env[key]) {
process.env[key] = JSON.parse(env[key]);
}
});
const http = require('http');
const express = require('express');
const app = express();
require('./server')(app);
const port = parseInt(process.env.PORT || 8080, 10);
const httpServer = http.createServer(app);
httpServer.listen(port, null, () => {
console.log(`HTTP server started: http://localhost:${port}`);
});
// Handle graceful shutdown
process.on('SIGTERM', () => {
httpServer.close(() => {
process.exit(0);
});
});

22028
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,141 +1,51 @@
{
"name": "stackedit",
"version": "5.15.21",
"description": "免费, 开源, 功能齐全的 Markdown 编辑器",
"author": "Benoit Schweblin, 豆萁",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/mafgwo/stackedit/issues"
"version": "4.3.22",
"description": "StackEdit is a free, open-source Markdown editor based on PageDown, the Markdown library used by Stack Overflow and the other Stack Exchange sites.",
"main": "res/main.js",
"engines": {
"node": "0.10.x"
},
"main": "index.js",
"scripts": {
"postinstall": "gulp build-prism",
"start": "node build/dev-server.js",
"build": "node build/build.js && npm run build-style",
"build-style": "webpack --config build/webpack.style.conf.js",
"lint": "eslint --ext .js,.vue src server",
"unit": "jest --config test/unit/jest.conf.js --runInBand",
"unit-with-coverage": "jest --config test/unit/jest.conf.js --runInBand --coverage",
"test": "npm run lint && npm run unit",
"preversion": "npm run test",
"postversion": "git push origin master --tags && npm publish",
"patch": "npm version patch -m \"Tag v%s\"",
"minor": "npm version minor -m \"Tag v%s\"",
"major": "npm version major -m \"Tag v%s\"",
"chart": "mkdir -p dist && rm -rf dist/stackedit && cp -r chart dist/stackedit && sed -i.bak -e s/STACKEDIT_VERSION/$npm_package_version/g dist/stackedit/*.yaml && rm dist/stackedit/*.yaml.bak"
"directories": {
"doc": "doc"
},
"dependencies": {
"@vue/test-utils": "^1.0.0-beta.16",
"abcjs": "^5.2.0",
"babel-runtime": "^6.26.0",
"bezier-easing": "^1.1.0",
"body-parser": "^1.18.2",
"clipboard": "^1.7.1",
"compression": "^1.7.0",
"diff-match-patch": "^1.0.0",
"file-saver": "^1.3.8",
"handlebars": "^4.0.10",
"indexeddbshim": "^3.6.2",
"js-yaml": "^3.11.0",
"katex": "^0.16.2",
"markdown-it": "^8.4.1",
"markdown-it-abbr": "^1.0.4",
"markdown-it-deflist": "^2.0.2",
"markdown-it-emoji": "^1.3.0",
"markdown-it-footnote": "^3.0.1",
"markdown-it-imsize": "^2.0.1",
"markdown-it-mark": "^2.0.0",
"markdown-it-pandoc-renderer": "1.1.3",
"markdown-it-sub": "^1.0.0",
"markdown-it-sup": "^1.0.0",
"mermaid": "^8.9.2",
"mousetrap": "^1.6.1",
"normalize-scss": "^7.0.1",
"prismjs": "^1.6.0",
"request": "^2.85.0",
"serve-static": "^1.13.2",
"tmp": "^0.0.33",
"turndown": "^7.1.1",
"vue": "^2.5.16",
"vuex": "^3.0.1"
"bower": "~1.8.2",
"compression": "~1.0.11",
"ejs": "~0.8.4",
"express": "~4.8.5",
"request": "~2.40.0",
"serve-static": "~1.6.5",
"ssh2": "~0.3.5"
},
"devDependencies": {
"autoprefixer": "^6.7.2",
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.3",
"babel-jest": "^21.0.2",
"babel-loader": "^7.1.4",
"babel-plugin-dynamic-import-node": "^1.2.0",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-polyfill": "^6.23.0",
"babel-preset-env": "^1.7.0",
"babel-preset-stage-2": "^6.22.0",
"babel-register": "^6.22.0",
"chalk": "^1.1.3",
"connect-history-api-fallback": "^1.3.0",
"copy-webpack-plugin": "^4.5.1",
"css-loader": "^0.28.11",
"eslint": "^4.19.1",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-friendly-formatter": "^4.0.1",
"eslint-import-resolver-webpack": "^0.9.0",
"eslint-loader": "^2.0.0",
"eslint-plugin-html": "^4.0.3",
"eslint-plugin-import": "^2.11.0",
"eventsource-polyfill": "^0.9.6",
"express": "^4.16.3",
"extract-text-webpack-plugin": "^2.0.0",
"favicons-webpack-plugin": "^0.0.9",
"file-loader": "^1.1.11",
"friendly-errors-webpack-plugin": "^1.7.0",
"gulp": "^4.0.2",
"gulp-concat": "^2.6.1",
"html-webpack-plugin": "^3.2.0",
"http-proxy-middleware": "^0.18.0",
"identity-obj-proxy": "^3.0.0",
"ignore-loader": "^0.1.2",
"jest": "^23.0.0",
"jest-raw-loader": "^1.0.1",
"jest-serializer-vue": "^0.3.0",
"js-md5": "^0.7.3",
"node-sass": "^4.0.0",
"npm-bump": "^0.0.23",
"offline-plugin": "^5.0.3",
"opn": "^4.0.2",
"optimize-css-assets-webpack-plugin": "^1.3.2",
"ora": "^1.2.0",
"raw-loader": "^0.5.1",
"replace-in-file": "^4.1.0",
"rimraf": "^2.6.0",
"sass-loader": "^7.0.1",
"semver": "^5.5.0",
"shelljs": "^0.8.1",
"string-replace-loader": "^2.1.1",
"stylelint": "^9.2.0",
"stylelint-config-standard": "^16.0.0",
"stylelint-processor-html": "^1.0.0",
"stylelint-webpack-plugin": "^0.10.4",
"url-loader": "^1.0.1",
"vue-jest": "^1.0.2",
"vue-loader": "^15.0.9",
"vue-style-loader": "^4.1.0",
"vue-template-compiler": "^2.5.16",
"webpack": "^2.6.1",
"webpack-bundle-analyzer": "^3.3.2",
"webpack-dev-middleware": "^1.10.0",
"webpack-hot-middleware": "^2.18.0",
"webpack-merge": "^4.1.2",
"webpack-pwa-manifest": "^3.7.1",
"worker-loader": "^1.1.1"
"gulp": "~3.9.1",
"gulp-requirejs": "~1.0.0",
"gulp-jshint": "~1.8.4",
"gulp-uglify": "~1.1.0",
"gulp-less": "~1.3.5",
"bower-requirejs": "~1.1.0",
"gulp-inject": "git://github.com/benweet/gulp-inject.git#8bd702d143a578e3b44290d82612ab808ee17281",
"run-sequence": "~0.3.6",
"gulp-clean": "~0.3.1",
"gulp-replace": "~0.4.0",
"gulp-bump": "~0.1.11",
"gulp-util": "~3.0.1",
"knox": "~0.9.1",
"mime": "~1.2.11",
"event-stream": "~3.1.7"
},
"engines": {
"node": ">= 8.0.0",
"npm": ">= 5.0.0"
"scripts": {
"postinstall": "bower --allow-root install",
"test": "echo \"Error: no test specified\" && exit 1"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 10"
]
"repository": {
"type": "git",
"url": "git://github.com/benweet/stackedit.git"
},
"author": "Benoit Schweblin",
"license": "Apache License",
"bugs": {
"url": "https://github.com/benweet/stackedit/issues"
}
}

14
public/.jshintrc Normal file
View File

@ -0,0 +1,14 @@
{
"curly": true,
"browser": true,
"devel": true,
"indent": 4,
"latedef": true,
"undef": true,
"unused": true,
"expr": true,
"globals": {
"define": false,
"require": false
}
}

BIN
public/Welcome document.pdf Normal file

Binary file not shown.

229
public/cache.manifest Normal file
View File

@ -0,0 +1,229 @@
CACHE MANIFEST
#Date Thu Feb 01 2018 23:51:19 GMT+0000 (WET)
CACHE:
.
editor
viewer
# start_inject_resources
res-min/main.js
res-min/require.js
res-min/font/PTSans-Bold-webfont.woff
res-min/font/PTSans-BoldItalic-webfont.woff
res-min/font/PTSans-Italic-webfont.woff
res-min/font/PTSans-Regular-webfont.woff
res-min/font/SourceCodePro-Bold-webfont.woff
res-min/font/SourceCodePro-Regular-webfont.woff
res-min/font/SourceSansPro-Bold-webfont.woff
res-min/font/SourceSansPro-BoldItalic-webfont.woff
res-min/font/SourceSansPro-Italic-webfont.woff
res-min/font/SourceSansPro-Light-webfont.woff
res-min/font/SourceSansPro-LightItalic-webfont.woff
res-min/font/SourceSansPro-Regular-webfont.woff
res-min/font/cursive_standard-webfont.woff
res-min/font/fontello.svg
res-min/font/fontello.woff
res-min/img/button.svg
res-min/img/code-block.png
res-min/img/comments.png
res-min/img/conflict.png
res-min/img/diagram.png
res-min/img/gittip.png
res-min/img/icons.png
res-min/img/icons2x.png
res-min/img/live-preview.png
res-min/img/logo-highres.png
res-min/img/logo-ipad-retina.png
res-min/img/logo.svg
res-min/img/math.png
res-min/img/menu-icon.png
res-min/img/menu.png
res-min/img/publish.png
res-min/img/stackedit-32.ico
res-min/img/syntax-highlighting.gif
res-min/img/toc.gif
res-min/themes/base.css
res-min/themes/blue.css
res-min/themes/default.css
res-min/themes/gray.css
res-min/themes/night.css
res-min/themes/school.css
res-min/themes/solarized-dark.css
res-min/themes/solarized-light.css
# end_inject_resources
# start_inject_mathjax
res/bower-libs/MathJax/MathJax.js?config=TeX-AMS_SVG
res/bower-libs/MathJax/config/Safe.js?rev=2.6.1
res/bower-libs/MathJax/config/TeX-AMS_SVG.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/jax.js?rev=2.6.1
res/bower-libs/MathJax/extensions/AssistiveMML.js?rev=2.6.1
res/bower-libs/MathJax/extensions/CHTML-preview.js?rev=2.6.1
res/bower-libs/MathJax/extensions/FontWarnings.js?rev=2.6.1
res/bower-libs/MathJax/extensions/HelpDialog.js?rev=2.6.1
res/bower-libs/MathJax/extensions/MatchWebFonts.js?rev=2.6.1
res/bower-libs/MathJax/extensions/MathEvents.js?rev=2.6.1
res/bower-libs/MathJax/extensions/MathMenu.js?rev=2.6.1
res/bower-libs/MathJax/extensions/MathZoom.js?rev=2.6.1
res/bower-libs/MathJax/extensions/Safe.js?rev=2.6.1
res/bower-libs/MathJax/extensions/asciimath2jax.js?rev=2.6.1
res/bower-libs/MathJax/extensions/fast-preview.js?rev=2.6.1
res/bower-libs/MathJax/extensions/jsMath2jax.js?rev=2.6.1
res/bower-libs/MathJax/extensions/mml2jax.js?rev=2.6.1
res/bower-libs/MathJax/extensions/tex2jax.js?rev=2.6.1
res/bower-libs/MathJax/extensions/toMathML.js?rev=2.6.1
res/bower-libs/MathJax/extensions/HTML-CSS/handle-floats.js?rev=2.6.1
res/bower-libs/MathJax/extensions/MathML/content-mathml.js?rev=2.6.1
res/bower-libs/MathJax/extensions/MathML/mml3.js?rev=2.6.1
res/bower-libs/MathJax/extensions/TeX/AMScd.js?rev=2.6.1
res/bower-libs/MathJax/extensions/TeX/AMSmath.js?rev=2.6.1
res/bower-libs/MathJax/extensions/TeX/AMSsymbols.js?rev=2.6.1
res/bower-libs/MathJax/extensions/TeX/HTML.js?rev=2.6.1
res/bower-libs/MathJax/extensions/TeX/action.js?rev=2.6.1
res/bower-libs/MathJax/extensions/TeX/autobold.js?rev=2.6.1
res/bower-libs/MathJax/extensions/TeX/autoload-all.js?rev=2.6.1
res/bower-libs/MathJax/extensions/TeX/bbox.js?rev=2.6.1
res/bower-libs/MathJax/extensions/TeX/begingroup.js?rev=2.6.1
res/bower-libs/MathJax/extensions/TeX/boldsymbol.js?rev=2.6.1
res/bower-libs/MathJax/extensions/TeX/cancel.js?rev=2.6.1
res/bower-libs/MathJax/extensions/TeX/color.js?rev=2.6.1
res/bower-libs/MathJax/extensions/TeX/enclose.js?rev=2.6.1
res/bower-libs/MathJax/extensions/TeX/extpfeil.js?rev=2.6.1
res/bower-libs/MathJax/extensions/TeX/mathchoice.js?rev=2.6.1
res/bower-libs/MathJax/extensions/TeX/mediawiki-texvc.js?rev=2.6.1
res/bower-libs/MathJax/extensions/TeX/mhchem.js?rev=2.6.1
res/bower-libs/MathJax/extensions/TeX/newcommand.js?rev=2.6.1
res/bower-libs/MathJax/extensions/TeX/noErrors.js?rev=2.6.1
res/bower-libs/MathJax/extensions/TeX/noUndefined.js?rev=2.6.1
res/bower-libs/MathJax/extensions/TeX/unicode.js?rev=2.6.1
res/bower-libs/MathJax/extensions/TeX/verb.js?rev=2.6.1
res/bower-libs/MathJax/jax/element/mml/jax.js?rev=2.6.1
res/bower-libs/MathJax/jax/element/mml/optable/Arrows.js?rev=2.6.1
res/bower-libs/MathJax/jax/element/mml/optable/BasicLatin.js?rev=2.6.1
res/bower-libs/MathJax/jax/element/mml/optable/CombDiacritMarks.js?rev=2.6.1
res/bower-libs/MathJax/jax/element/mml/optable/CombDiactForSymbols.js?rev=2.6.1
res/bower-libs/MathJax/jax/element/mml/optable/Dingbats.js?rev=2.6.1
res/bower-libs/MathJax/jax/element/mml/optable/GeneralPunctuation.js?rev=2.6.1
res/bower-libs/MathJax/jax/element/mml/optable/GeometricShapes.js?rev=2.6.1
res/bower-libs/MathJax/jax/element/mml/optable/GreekAndCoptic.js?rev=2.6.1
res/bower-libs/MathJax/jax/element/mml/optable/Latin1Supplement.js?rev=2.6.1
res/bower-libs/MathJax/jax/element/mml/optable/LetterlikeSymbols.js?rev=2.6.1
res/bower-libs/MathJax/jax/element/mml/optable/MathOperators.js?rev=2.6.1
res/bower-libs/MathJax/jax/element/mml/optable/MiscMathSymbolsA.js?rev=2.6.1
res/bower-libs/MathJax/jax/element/mml/optable/MiscMathSymbolsB.js?rev=2.6.1
res/bower-libs/MathJax/jax/element/mml/optable/MiscSymbolsAndArrows.js?rev=2.6.1
res/bower-libs/MathJax/jax/element/mml/optable/MiscTechnical.js?rev=2.6.1
res/bower-libs/MathJax/jax/element/mml/optable/SpacingModLetters.js?rev=2.6.1
res/bower-libs/MathJax/jax/element/mml/optable/SuppMathOperators.js?rev=2.6.1
res/bower-libs/MathJax/jax/element/mml/optable/SupplementalArrowsA.js?rev=2.6.1
res/bower-libs/MathJax/jax/element/mml/optable/SupplementalArrowsB.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/autoload/annotation-xml.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/autoload/maction.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/autoload/menclose.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/autoload/mglyph.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/autoload/mmultiscripts.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/autoload/ms.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/autoload/mtable.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/autoload/multiline.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Typewriter/Regular/BasicLatin.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Typewriter/Regular/CombDiacritMarks.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Typewriter/Regular/Main.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Typewriter/Regular/Other.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Size4/Regular/Main.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Size3/Regular/Main.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/fontdata-extra.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/fontdata.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/AMS/Regular/Arrows.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/AMS/Regular/BoxDrawing.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/AMS/Regular/CombDiacritMarks.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/AMS/Regular/Dingbats.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/AMS/Regular/EnclosedAlphanum.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/AMS/Regular/GeneralPunctuation.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/AMS/Regular/GeometricShapes.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/AMS/Regular/GreekAndCoptic.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/AMS/Regular/Latin1Supplement.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/AMS/Regular/LatinExtendedA.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/AMS/Regular/LetterlikeSymbols.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/AMS/Regular/Main.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/AMS/Regular/MathOperators.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/AMS/Regular/MiscMathSymbolsB.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/AMS/Regular/MiscSymbols.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/AMS/Regular/MiscTechnical.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/AMS/Regular/PUA.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/AMS/Regular/SpacingModLetters.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/AMS/Regular/SuppMathOperators.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Caligraphic/Bold/Main.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Caligraphic/Regular/Main.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Fraktur/Bold/BasicLatin.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Fraktur/Bold/Main.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Fraktur/Bold/Other.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Fraktur/Bold/PUA.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Fraktur/Regular/BasicLatin.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Fraktur/Regular/Main.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Fraktur/Regular/Other.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Fraktur/Regular/PUA.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Bold/Arrows.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Bold/BasicLatin.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Bold/CombDiacritMarks.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Bold/CombDiactForSymbols.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Bold/GeneralPunctuation.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Bold/GeometricShapes.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Bold/GreekAndCoptic.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Bold/Latin1Supplement.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Bold/LatinExtendedA.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Bold/LatinExtendedB.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Bold/LetterlikeSymbols.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Bold/Main.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Bold/MathOperators.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Bold/MiscMathSymbolsA.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Bold/MiscSymbols.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Bold/MiscTechnical.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Bold/SpacingModLetters.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Bold/SuppMathOperators.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Bold/SupplementalArrowsA.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Regular/BasicLatin.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Regular/CombDiacritMarks.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Regular/GeometricShapes.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Regular/GreekAndCoptic.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Regular/LatinExtendedA.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Regular/LatinExtendedB.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Regular/LetterlikeSymbols.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Regular/Main.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Regular/MathOperators.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Regular/MiscSymbols.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Regular/SpacingModLetters.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Regular/SuppMathOperators.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Italic/BasicLatin.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Italic/CombDiacritMarks.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Italic/GeneralPunctuation.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Italic/GreekAndCoptic.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Italic/LatinExtendedA.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Italic/LatinExtendedB.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Italic/LetterlikeSymbols.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Italic/Main.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Main/Italic/MathOperators.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Math/BoldItalic/Main.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Math/Italic/Main.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/SansSerif/Bold/BasicLatin.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/SansSerif/Bold/CombDiacritMarks.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/SansSerif/Bold/Main.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/SansSerif/Bold/Other.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/SansSerif/Italic/BasicLatin.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/SansSerif/Italic/CombDiacritMarks.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/SansSerif/Italic/Main.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/SansSerif/Italic/Other.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/SansSerif/Regular/BasicLatin.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/SansSerif/Regular/CombDiacritMarks.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/SansSerif/Regular/Main.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/SansSerif/Regular/Other.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Script/Regular/BasicLatin.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Script/Regular/Main.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Size2/Regular/Main.js?rev=2.6.1
res/bower-libs/MathJax/jax/output/SVG/fonts/TeX/Size1/Regular/Main.js?rev=2.6.1
# end_inject_mathjax
NETWORK:
*

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
google-site-verification: google4971b5a4d775691a.html

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../libs/dropbox.min.js"></script>
<script type="text/javascript">
Dropbox.AuthDriver.Popup.oauthReceiver();
</script>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html manifest="cache.manifest">
<head>
<script type="text/javascript">
var redirectUrl = location.href.substring(0, location.href.indexOf("html/gdrive-action.html")) + 'editor';
var state = decodeURI((/state=(.+?)(&|$)/
.exec(location.search) || [ , null ])[1]);
if(state) {
localStorage["gdrive.state"] = state;
}
window.location.replace(redirectUrl);
</script>
</head>
</html>

View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script type="text/javascript">
function getParameter(name) {
var regex = new RegExp(name + "=(.+?)(&|$)");
try {
return decodeURI(regex.exec(location.search)[1]);
} catch (e) {
return undefined;
}
}
var client_id = getParameter("client_id");
var scope = getParameter("scope") || 'repo,gist';
var code = getParameter("code");
if (client_id) {
window.location.href = "https://github.com/login/oauth/authorize?client_id="
+ client_id + "&scope=" + scope;
} else {
if (code) {
localStorage["githubCode"] = code;
}
window.close();
}
</script>
</head>
</html>

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