├── .gitignore
├── screenshot
├── 1.jpg
└── 2.jpg
├── views
├── public
│ ├── fonts
│ │ ├── glyphicons-halflings-regular.eot
│ │ ├── glyphicons-halflings-regular.ttf
│ │ ├── glyphicons-halflings-regular.woff
│ │ └── glyphicons-halflings-regular.woff2
│ ├── css
│ │ └── style.min.css
│ └── js
│ │ ├── entry.min.js
│ │ └── jquery.min.js
├── ejs
│ ├── tpl
│ │ ├── file-content.ejs
│ │ ├── new-post.ejs
│ │ └── list.ejs
│ └── index.ejs
├── scss
│ └── style.scss
└── js
│ └── entry.js
├── webpack.config.js
├── package.json
├── app.js
├── route
└── route.json
├── readme.md
├── module
├── config-init.js
└── get-all-data.js
├── controller
├── hexo.js
└── index.js
└── bin
└── hexo-admin.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | node_modules/
3 | __siteDB.json
4 |
--------------------------------------------------------------------------------
/screenshot/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geekwen/hexo-local-admin/HEAD/screenshot/1.jpg
--------------------------------------------------------------------------------
/screenshot/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geekwen/hexo-local-admin/HEAD/screenshot/2.jpg
--------------------------------------------------------------------------------
/views/public/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geekwen/hexo-local-admin/HEAD/views/public/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/views/public/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geekwen/hexo-local-admin/HEAD/views/public/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/views/public/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geekwen/hexo-local-admin/HEAD/views/public/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/views/public/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geekwen/hexo-local-admin/HEAD/views/public/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * webpack 进行打包构建
3 | * 在控制台执行:
4 | *
5 | * 'webpack' 即可打包
6 | * 'webpack --watch' 监听文件状态
7 | * 'webpack --display-error-details' 显示错误信息
8 | * 'webpack --progress' 显示执行进度
9 | *
10 | * for more: http://webpack.github.io/docs/cli.htm
11 | * */
12 |
13 | // var webpack = require('webpack');
14 | module.exports = {
15 | entry: {'./views/public/js/entry': './views/js/entry.js'},
16 | output: {
17 | path: './',
18 | filename: '[name].min.js'
19 | },
20 | module: {
21 | loaders: [
22 | {test: /\.ejs$/, loader: 'ejs-compiled'}
23 | ]
24 | }
25 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hexo-local-admin",
3 | "version": "1.3.8",
4 | "description": "A hexo local admin tool",
5 | "main": "app.js",
6 | "author": "geekwen",
7 | "license": "MIT",
8 | "scripts": {
9 | "start": "node app.js",
10 | "test": "echo \"Error: no test specified\" && exit 1",
11 | "build": "webpack --progress -p",
12 | "debug": "webpack --progress -d --watch"
13 | },
14 | "bin": {
15 | "hexo-admin": "./bin/hexo-admin.js"
16 | },
17 | "keywords": [
18 | "hexo",
19 | "admin"
20 | ],
21 | "repository": {
22 | "type": "git",
23 | "url": "https://github.com/geekwen/hexo-local-admin.git"
24 | },
25 | "dependencies": {
26 | "body-parser": "^1.15.2",
27 | "commander": "^2.9.0",
28 | "ejs": "^2.4.2",
29 | "ejs-compiled-loader": "^2.1.1",
30 | "express": "^4.14.0",
31 | "extend": "^3.0.0",
32 | "hexo-front-matter": "^0.2.3",
33 | "log4js": "^0.6.38",
34 | "node-dir": "^0.1.17",
35 | "tree-kill": "^1.1.0"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | const PORT = 4001,
2 | EXPRESS = require('express'),
3 | BODY_PARSER = require('body-parser'),
4 | FS = require('fs'),
5 | PATH = require('path'),
6 | LOGGER = require('log4js').getLogger(),
7 | APP = EXPRESS();
8 |
9 | APP.set('view engine', 'ejs');
10 | APP.set('views', PATH.join(__dirname, 'views/ejs'));
11 | APP.use(EXPRESS.static('views/public'));
12 | APP.use(BODY_PARSER.urlencoded({extended: true}));
13 | APP.use(BODY_PARSER.json());
14 |
15 | require('./route/route.json').forEach(function (route) {
16 | APP[route.method](route.path, require(PATH.join(__dirname, 'controller', route.module))[route.handler])
17 | });
18 |
19 | APP.listen(PORT, function () {
20 | LOGGER.info('Hexo local admin is working! Please visit http://localhost:' + PORT);
21 | LOGGER.info('press CTL + C to stop');
22 | });
23 |
24 | // 启动服务时默认跑一边数据
25 | require('./module/get-all-data').updateDBFile();
26 |
27 | process.on('SIGINT', function() {
28 | console.log('\nBye, hope to see you again.');
29 | process.exit(0);
30 | });
31 |
--------------------------------------------------------------------------------
/route/route.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "path": "/",
4 | "method": "get",
5 | "module": "index",
6 | "handler": "entry"
7 | },
8 | {
9 | "path": "/get-all",
10 | "method": "get",
11 | "module": "index",
12 | "handler": "getAll"
13 | },
14 | {
15 | "path": "/get-markdown-file",
16 | "method": "get",
17 | "module": "index",
18 | "handler": "getMarkdownFile"
19 | },
20 | {
21 | "path": "/get-config",
22 | "method": "get",
23 | "module": "index",
24 | "handler": "getConfig"
25 | },
26 | {
27 | "path": "/write-markdown-file",
28 | "method": "post",
29 | "module": "index",
30 | "handler": "writeMarkdownFile"
31 | },
32 | {
33 | "path": "/move-markdown-file",
34 | "method": "post",
35 | "module": "index",
36 | "handler": "moveMarkdownFile"
37 | },
38 | {
39 | "path": "/hexo-server",
40 | "method": "get",
41 | "module": "hexo",
42 | "handler": "server"
43 | },
44 | {
45 | "path": "/hexo-kill",
46 | "method": "get",
47 | "module": "hexo",
48 | "handler": "kill"
49 | },
50 | {
51 | "path": "/hexo-deploy",
52 | "method": "get",
53 | "module": "hexo",
54 | "handler": "deploy"
55 | }
56 | ]
--------------------------------------------------------------------------------
/views/ejs/tpl/file-content.ejs:
--------------------------------------------------------------------------------
1 | <% if (data.type) { %>
2 |
4 | - Edit
5 |
6 | <% if (data.type == 'posts' || data.type == 'pages'){ %>
7 | - Move to draft
8 | <% } else if (data.type == 'drafts') { %>
9 | - Publish to post
10 | - Publish to page
11 | <% } else if (data.type == 'trash') { %>
12 | - Restore to post
13 | - Restore to page
14 | - Restore to draft
15 | <% } %>
16 |
17 | <% if (data.type === 'trash') { %>
18 | - Delete
19 | <% }else { %>
20 | - Move to trash
21 | <% } %>
22 |
23 | <% } %>
24 | <%
25 | var data_content = 'no content';
26 | if (data.raw_content) {
27 | data_content = data.raw_content.trim();
28 | } else if (typeof data == 'string') {
29 | data_content = data.trim()
30 | }
31 | %>
32 | <% if (data.page_url) { %>
33 | /<%= data.page_url %>
34 | <% } else if (data.title) { %>
35 | <%= data.title %>
36 | <% } %>
37 |
38 | <% if (data.file_path) { %>
39 | <%= data.file_path %>
40 | <% } %>
41 |
42 |
43 |
44 | <%= data_content %>
--------------------------------------------------------------------------------
/views/ejs/tpl/new-post.ejs:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
12 |
16 |
17 |
21 |
22 |
23 |
24 | <%
25 | var time = new Date();
26 | time = time.getFullYear() + '-' + (time.getMonth() + 1) + '-' + time.getDate() + ' ' +
27 | (time.getHours().toString().length === 1 ? '0' + time.getHours() : time.getHours()) + ':' +
28 | (time.getMinutes().toString().length === 1 ? '0' + time.getMinutes() : time.getMinutes()) + ':' +
29 | (time.getSeconds().toString().length === 1 ? '0' + time.getSeconds() : time.getSeconds());
30 | %>
31 |
32 |
33 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # hexo local admin
2 |
3 | This is a hexo site admin tool(works only on your local machine.). You can use this tool to manage your posts, pages, drafts and trash, even can launch hexo server and deploy your site! Try it out!
4 |
5 | 
6 |
7 | 
8 |
9 | **Requirement**
10 |
11 | node version 4.x or later
12 |
13 | **Support**
14 |
15 | OS: windows and unix-like
16 |
17 | Browser: highly recommend **latest** chrome and firefox. also support any other **latest** modern browsers.
18 |
19 | ## Before start
20 |
21 | 0. **Please backup your `_source` directory, just for safety**
22 |
23 | 1. The deploy button means run `hexo deploy -g` command. So before you use this feature, please make sure you can manually use this command to deploy your site. More info about hexo deploy: [hexo doc: deployment](https://hexo.io/docs/deployment.html)
24 |
25 | 2. (\*nix os) if you can't set root path or theme name, try add `sudo` before command.
26 |
27 | ## HOW-TO-USE
28 |
29 | 1. install
30 | ``` shell
31 | npm install -g hexo-local-admin
32 | ```
33 |
34 | 2. use command
35 |
36 | ```
37 | hexo-admin -h
38 | ```
39 | will get:
40 | ```
41 | Usage: hexo-admin [options] [command]
42 | Commands:
43 | start Start hexo local admin server
44 | Options:
45 | -h, --help output usage information
46 | -V, --version output the version number
47 | -r, --root-path [root_path] view/set hexo root path
48 | -t, --theme-name [theme_name] view/set hexo theme name
49 |
50 | ```
51 |
52 | 3. enjoy!
53 |
54 | ## License
55 |
56 | MIT
--------------------------------------------------------------------------------
/module/config-init.js:
--------------------------------------------------------------------------------
1 | const FS = require('fs'),
2 | PATH = require('path'),
3 | OS = require('os'),
4 | LOGGER = require('log4js').getLogger();
5 |
6 | var configPath;
7 |
8 | /^win/.test(OS.platform()) ?
9 | configPath = PATH.join(OS.homedir(), 'Documents', '.hexo-local-admin-config.json') :
10 | configPath = PATH.join(OS.homedir(), '.hexo-local-admin-config.json');
11 |
12 | try {
13 | config = require(configPath);
14 | }
15 | catch (e) {
16 | try {
17 | config = {
18 | rootPath: '.',
19 | theme: '.'
20 | };
21 | FS.writeFileSync(configPath, JSON.stringify(config), 'utf-8');
22 | }
23 | catch (e) {
24 | throw e;
25 | }
26 | }
27 |
28 | isPathReady();
29 |
30 | function isPathReady() {
31 | if (config.rootPath === '.') {
32 | let msg = '\n--------\nERROR!!!\nhexo root path is not set!\ntry "hexo-admin -r your-hexo-path" to set hexo root path\n--------';
33 | LOGGER.warn(msg);
34 | return {
35 | status: false,
36 | msg: msg
37 | };
38 | }
39 |
40 | if (config.theme === '.') {
41 | let msg = '\n--------\nERROR!!!\nhexo theme name is not set!\ntry "hexo-admin -t your-theme-name" to set hexo theme\n--------';
42 | LOGGER.warn(msg);
43 | return {
44 | status: false,
45 | msg: msg
46 | };
47 | }
48 |
49 | try {
50 | FS.accessSync(config.rootPath, FS.F_OK);
51 | FS.accessSync(PATH.join(config.rootPath, 'themes', config.theme), FS.F_OK);
52 | return {
53 | status: true,
54 | msg: 'config is set!'
55 | };
56 | }
57 | catch (error) {
58 | throw error;
59 | }
60 | }
61 |
62 | function data() {
63 | config.configPath = configPath;
64 | config.adminPath = PATH.join(__dirname, '..');
65 | config.siteConfig = PATH.join(config.rootPath, '_config.yml');
66 | config.themeConfig = PATH.join(config.rootPath, 'themes', config.theme, '_config.yml');
67 | config.sourcePath = PATH.join(config.rootPath, 'source');
68 | config.postPath = PATH.join(config.sourcePath, '_posts');
69 | config.draftPath = PATH.join(config.sourcePath, '_drafts');
70 | config.trashPath = PATH.join(config.sourcePath, '_trash');
71 | return config;
72 | }
73 |
74 | exports.isPathReady = isPathReady;
75 | exports.data = data;
76 |
--------------------------------------------------------------------------------
/views/ejs/tpl/list.ejs:
--------------------------------------------------------------------------------
1 | <% if (typeof type === 'undefined') {
2 | var type = 'posts';
3 | } %>
4 |
18 |
19 | <% var contentType = ['posts', 'pages', 'drafts', 'trash']; %>
20 | <% for (var i = 0; i < contentType.length; i++) { %>
21 |
22 | <% if (data[contentType[i]].length > 0){ %>
23 |
24 | <% data[contentType[i]].forEach(function(item, index) { %>
25 |
26 |
29 |
30 | <% if (item.file_name) { %>
31 |
32 |
33 | <%= item.file_name %>
34 |
35 | <% } %>
36 |
37 | <% if (contentType[i] === 'pages'){ %>
38 | <% if (item.page_url){ %>
39 |
<%= item.page_url %>
40 | <% } %>
41 | <% } else { %>
42 | <% if (item.title){ %>
43 |
<%= item.title %>
44 | <% } %>
45 | <% } %>
46 |
47 | <% if (item.date) { %>
48 |
52 | <% } %>
53 |
54 | <% if (item.tags) { %>
55 |
56 |
57 | <% if (Array.isArray(item.tags)) { %>
58 | <% item.tags.forEach(function(tagName) { %>
59 | #<%= tagName %>
60 | <% }); %>
61 | <% } else { %>
62 | #<%= item.tags %>
63 | <% } %>
64 |
65 | <% } %>
66 |
67 |
68 | <% }); %>
69 |
70 | <% } else { %>
71 |
72 |
75 |
76 | <% } %>
77 |
78 | <% } %>
--------------------------------------------------------------------------------
/controller/hexo.js:
--------------------------------------------------------------------------------
1 | const CHILD_PROCESS = require('child_process'),
2 | LOGGER = require('log4js').getLogger(),
3 | HEXO_PATH = require('../module/config-init').data(),
4 | TREE_KILL = require('tree-kill');
5 |
6 | var hexo_server = null,
7 | isWin = /^win/.test(process.platform),
8 | hexo_pid = NaN;
9 | hexo_cli = isWin ? 'hexo.cmd' : 'hexo';
10 |
11 | exports.server = function (req, res) {
12 | try {
13 | if (!Number.isNaN(hexo_pid)) killHexo();
14 |
15 | // 启动时先 clean 一下
16 | var clean = CHILD_PROCESS.spawn(
17 | hexo_cli,
18 | ['clean'],
19 | {cwd: HEXO_PATH.rootPath}
20 | );
21 |
22 | clean.stdout.on('data', function (data) {
23 | LOGGER.info(data.toString('utf8'));
24 | });
25 |
26 | clean.on('exit', function () {
27 | LOGGER.info('hexo cleaned!');
28 |
29 | hexo_server = CHILD_PROCESS.spawn(
30 | hexo_cli,
31 | ['server'],
32 | {cwd: HEXO_PATH.rootPath}
33 | );
34 |
35 | hexo_pid = hexo_server.pid;
36 | LOGGER.info('hexo pid:' + hexo_pid);
37 |
38 | hexo_server.stdout.on('data', function (data) {
39 | LOGGER.info(data.toString('utf8'));
40 | });
41 |
42 | res.json({"status": "success"});
43 | });
44 | }
45 | catch (e) {
46 | res.status(500).send({"status": "error", "msg": "hexo server launch failed!"});
47 | }
48 | };
49 |
50 | exports.kill = function (req, res) {
51 | try {
52 | killHexo();
53 | res.json({"status": "success"});
54 | }
55 | catch
56 | (e) {
57 | res.status(500).send(e);
58 | }
59 | };
60 |
61 | exports.deploy = function (req, res) {
62 | try {
63 | var hexo_deploy = CHILD_PROCESS.spawn(
64 | hexo_cli,
65 | ['deploy', '-g'],
66 | {cwd: HEXO_PATH.rootPath}
67 | );
68 |
69 | hexo_deploy.stdout.on('data', function (data) {
70 | LOGGER.info(data.toString('utf8'));
71 | });
72 |
73 | hexo_deploy.on('exit', function () {
74 | LOGGER.info('hexo deployed!');
75 | res.json({"status": "success"});
76 | });
77 | }
78 | catch (e) {
79 | res.status(500).send({"status": "error", "msg": "hexo deploy failed!"});
80 | }
81 | };
82 |
83 | function killHexo() {
84 | if (isWin) {
85 | TREE_KILL(hexo_pid, 'SIGTERM', function (err) {
86 | if (err) {
87 | LOGGER.error('kill hexo failed. hexo pid:' + hexo_pid + ';error=' + JSON.stringify(err));
88 | return;
89 | }
90 | LOGGER.info('hexo stopped');
91 | hexo_pid = NaN;
92 | });
93 | }
94 | else {
95 | hexo_server.kill();
96 | hexo_pid = NaN;
97 | LOGGER.info('hexo stopped');
98 | }
99 | }
--------------------------------------------------------------------------------
/bin/hexo-admin.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const PROGRAM = require('commander'),
4 | FS = require('fs'),
5 | PATH = require('path'),
6 | CHILD_PROCESS = require('child_process'),
7 | LOGGER = require('log4js').getLogger(),
8 | CONFIG_INIT = require('../module/config-init'),
9 | OS = require('os');
10 |
11 | var config = CONFIG_INIT.data(),
12 | data;
13 |
14 | PROGRAM.version(require('../package.json').version);
15 |
16 | PROGRAM.command('start')
17 | .description('Start hexo local admin server')
18 | .action(start);
19 |
20 | PROGRAM
21 | .option('-r, --root-path [root_path]', 'view/set hexo root path')
22 | .option('-t, --theme-name [theme_name]', 'view/set hexo theme name');
23 |
24 | PROGRAM.parse(process.argv);
25 |
26 | function start() {
27 | var isPathReady = CONFIG_INIT.isPathReady();
28 | if (!isPathReady.status) {
29 | console.log(isPathReady.msg);
30 | process.exit(1);
31 | }
32 | else {
33 | LOGGER.info('ready to start');
34 | var app = CHILD_PROCESS.fork(
35 | 'app.js',
36 | [],
37 | {cwd: PATH.join(__dirname, '..')}
38 | );
39 |
40 | LOGGER.info("pid:" + app.pid);
41 |
42 | app.on('error', function (e) {
43 | console.log(e);
44 | });
45 | }
46 | }
47 |
48 | if (typeof PROGRAM.rootPath === 'string' && typeof PROGRAM.themeName === 'string') {
49 | data = {
50 | rootPath: PROGRAM.rootPath,
51 | theme: PROGRAM.themeName
52 | };
53 |
54 | try {
55 | FS.writeFileSync(config.configPath, JSON.stringify(data), 'utf-8');
56 | console.log('Hexo root path is set to:' + PROGRAM.rootPath);
57 | console.log('Hexo theme is set to:' + PROGRAM.themeName);
58 | }
59 | catch (error) {
60 | throw error;
61 | }
62 | process.exit(0);
63 | }
64 |
65 | if (PROGRAM.rootPath) {
66 | var rootPath = PROGRAM.rootPath;
67 | if (typeof rootPath === 'boolean') {
68 | config.rootPath === '.' ?
69 | console.log('Currently hexo root path is not set.') :
70 | console.log('Currently hexo root path is:' + config.rootPath);
71 | }
72 | else {
73 | data = {
74 | rootPath: rootPath,
75 | theme: config.theme
76 | };
77 |
78 | try {
79 | FS.writeFileSync(config.configPath, JSON.stringify(data), 'utf-8');
80 | console.log('Hexo root path is set to:' + rootPath);
81 | }
82 | catch (error) {
83 | throw error;
84 | }
85 | }
86 | }
87 |
88 | if (PROGRAM.themeName) {
89 | var themeName = PROGRAM.themeName;
90 | if (typeof themeName === 'boolean') {
91 | config.theme === '.' ?
92 | console.log('Currently hexo theme is not set.') :
93 | console.log('Currently hexo theme is:' + config.theme);
94 | }
95 | else {
96 | data = {
97 | rootPath: config.rootPath,
98 | theme: themeName
99 | };
100 |
101 | try {
102 | FS.writeFileSync(config.configPath, JSON.stringify(data), 'utf-8');
103 | console.log('Hexo theme is set to:' + themeName);
104 | }
105 | catch (error) {
106 | throw error;
107 | }
108 | }
109 | }
110 |
111 |
--------------------------------------------------------------------------------
/views/ejs/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Hexo Admin
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
×
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | - Hexo PATH:
29 | - <%= data.hexoPath %>
30 |
31 |
32 |
33 |
34 | - Hexo theme:
35 | - <%= data.theme %>
36 |
37 |
38 |
39 |
40 |
41 |
42 | <% if (data) { %>
43 |
44 |
45 |
46 |
47 |
48 |
49 |
Actions
50 |
51 |
52 |
56 |
59 |
62 |
63 |
64 |
65 |
66 |
67 |
Hexo site actions
68 |
69 |
70 |
71 | Hexo is running! Visit
72 |
73 |
77 |
81 |
85 |
86 |
87 |
88 |
89 |
90 |
<%= data.tags.length %> tags
91 |
92 |
93 |
94 | <% for (var key in data.tags){ if (data.tags.hasOwnProperty(key) && key !== 'length'){ %>
95 | - <%= key %> ( <%= data.tags[key].length %> )
96 | <% }} %>
97 |
98 |
99 |
100 |
101 |
102 |
103 | <% include tpl/list.ejs %>
104 |
105 |
106 |
107 |
108 | <% } else { %>
109 | no content
110 | <% } %>
111 |
112 |
118 |
119 |
120 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/views/public/css/style.min.css:
--------------------------------------------------------------------------------
1 | *:focus,*:active{outline:none !important}pre{white-space:pre-line}@media (min-width: 768px){#content-detail-wrap,header,main,footer{padding-left:calc(100vw - 100%)}}header h1 a{color:inherit}header h1 a:hover{color:inherit;text-decoration:none}footer{padding-top:50px;padding-bottom:10px;font-size:12px;color:#ccc}.no-scroll{overflow:hidden}.panel-body button{margin:3px 2px}.form-control{border-radius:0;box-shadow:none;border-color:#eee;padding-left:8px;padding-right:8px}.form-control:focus,.form-control:active{border-color:#eee;box-shadow:none}#list-nav{margin-bottom:20px}#list-nav ~ [data-type]{display:none}#list-nav[data-select="posts"] li[data-type="posts"] a{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}#list-nav[data-select="posts"] ~ [data-type="posts"]{display:block}#list-nav[data-select="pages"] li[data-type="pages"] a{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}#list-nav[data-select="pages"] ~ [data-type="pages"]{display:block}#list-nav[data-select="drafts"] li[data-type="drafts"] a{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}#list-nav[data-select="drafts"] ~ [data-type="drafts"]{display:block}#list-nav[data-select="trash"] li[data-type="trash"] a{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}#list-nav[data-select="trash"] ~ [data-type="trash"]{display:block}.list-action li{cursor:pointer}.f-item{position:relative;padding:15px 20px;overflow:hidden;transition:all .4s;cursor:pointer}.f-item:hover{background-color:#f1f1f1}.f-item:hover .delete{opacity:1;visibility:visible}.f-item h4{max-height:50px;overflow:hidden;margin-top:5px;margin-bottom:8px;line-height:1.4}.f-item time,.f-item span,.f-item small{color:#bbb;font-size:12px;vertical-align:text-top}.f-item .tags{margin-top:2px}.f-item .delete{position:absolute;top:50%;right:10px;z-index:10;opacity:0;visibility:hidden;transform:translate(0, -50%);transition:all .4s}.f-item[data-type="pages"] h4::before{content:"/"}#content-detail-wrap{position:fixed;top:0;left:0;z-index:100;width:100%;height:100%;overflow-y:scroll;background-color:rgba(255,255,255,0.5);visibility:hidden;opacity:0;transition:all .4s}#content-detail-wrap.show{visibility:visible;opacity:1}#content-detail-wrap.show #content-detail-inner{top:0}#content-detail-wrap.show ~ header,#content-detail-wrap.show ~ main,#content-detail-wrap.show ~ footer{-webkit-filter:blur(5px);filter:blur(5px)}#content-detail-inner{position:relative;top:-50px;margin:50px 20px;max-width:700px;padding-top:20px;padding-bottom:20px;min-height:calc(100vh - 100px);background-color:#ffffff;border:1px solid #eee;transition:all .5s}@media (min-width: 768px){#content-detail-inner{width:70%;margin-left:auto;margin-right:auto;padding-top:50px;padding-bottom:50px}}#content-detail-inner>div{padding-left:20px;padding-right:20px}@media (min-width: 768px){#content-detail-inner>div{padding-left:40px;padding-right:40px}}#content-detail-inner.processing::before,#content-detail-inner.processing #content-detail-body{-webkit-filter:blur(4px);filter:blur(4px)}#content-detail-inner.processing::after{content:'Sending content...';display:block;position:absolute;top:0;left:0;z-index:10;width:100%;height:100%;text-align:center;line-height:100vh}#content-detail-inner[data-type]::before{content:"";position:absolute;top:20px;left:-1px;z-index:10;padding:5px 12px;border:1px solid #eee;background-color:#f1f1f1}#content-detail-inner[data-type="posts"]::before{content:"Post"}#content-detail-inner[data-type="pages"]::before{content:"Page"}#content-detail-inner[data-type="drafts"]::before{content:"Draft"}#content-detail-inner[data-type="trash"]::before{content:"Trash"}#content-detail-inner[data-type="new"]::before{content:"New"}#content-detail{white-space:pre-line}#content-detail-body{position:relative;margin-top:20px}#content-detail-body textarea{display:block;width:100%;min-height:50vh;margin-top:10px;padding:8px;border:1px solid #eee;resize:vertical;line-height:1.4}#content-tool{margin-top:50px;transition:all .4s}@media (min-width: 768px){#content-tool{position:fixed;left:80%;top:20%;margin-top:0;z-index:-1}}@media (min-width: 1000px){#content-tool{left:calc(50% + 300px)}}#content-tool li{position:relative;display:inline-block;margin:5px;padding:8px 12px;border:1px solid #eee;background-color:#f1f1f1;transition:all .4s ease-in-out}@media (min-width: 768px){#content-tool li{float:left;clear:left;padding-left:50px}}#content-tool li.collapse{transform:translateX(-100%)}#t-save.processing{color:transparent}#t-save.processing::after{display:block;content:"";width:20px;height:20px;position:absolute;top:50%;right:12px;border-radius:50%;border:2px solid #999;border-top-color:transparent;transform:translateY(-50%);animation:spin 1s linear infinite}#close-detail{position:absolute;right:10px;top:5px;z-index:20;padding:10px;line-height:1;font-size:20px;cursor:pointer}#alert{max-width:80%;position:fixed;top:0;left:50%;z-index:999;padding-top:10px;padding-bottom:10px;transition:all .4s;transform:translate(-50%, -100%);word-break:break-all}#alert.show{transform:translate(-50%, 0)}@media (min-width: 768px){#alert{max-width:400px}}#hexo-actions #visit-hexo,#hexo-actions #cli-hexo-stop{display:none}#hexo-actions.running #visit-hexo{display:block}#hexo-actions.running #cli-hexo-stop{display:inline-block}#hexo-actions.running #cli-hexo-server{display:none}#visit-hexo{margin-bottom:10px;padding:0 6px}@keyframes spin{0%{transform:translateY(-50%) rotate(0deg)}50%{transform:translateY(-50%) rotate(180deg)}100%{transform:translateY(-50%) rotate(360deg)}}
2 |
--------------------------------------------------------------------------------
/module/get-all-data.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 获取source文件夹下的文件,分析整个站点的数据内容
3 | * @module getAllData
4 | */
5 |
6 | const HEXO_PATH = require('../module/config-init').data(),
7 | FRONT_MATTER = require('hexo-front-matter'),
8 | FS = require('fs'),
9 | DIR = require('node-dir'),
10 | PATH = require('path'),
11 | EXTEND = require('extend'),
12 | LOGGER = require('log4js').getLogger();
13 |
14 | exports.updateDBFile = function () {
15 | let flagIndex = 0;
16 |
17 | // 初始化 _posts, _drafts, _trash文件夹
18 | ['post', 'draft', 'trash'].forEach(function (item) {
19 | FS.access(
20 | HEXO_PATH[item + 'Path'],
21 | FS.F_OK,
22 | function (err) {
23 | if (err) {
24 | FS.mkdir(
25 | HEXO_PATH[item + 'Path'],
26 | function (e) {
27 | if (e) throw e;
28 | flagIndex++;
29 | if (flagIndex === 3) action();
30 | }
31 | );
32 | return;
33 | }
34 | flagIndex++;
35 | if (flagIndex === 3) action();
36 | }
37 | );
38 | });
39 |
40 | function action() {
41 | let loadingProcess = 0,
42 | siteData = {
43 | hexoPath: HEXO_PATH.rootPath,
44 | theme: HEXO_PATH.theme,
45 | themeConfig: {
46 | title: 'Theme Config',
47 | file_path: HEXO_PATH.themeConfig,
48 | raw_content: getFile(HEXO_PATH.themeConfig)
49 | },
50 | siteConfig: {
51 | title: 'Site Config',
52 | file_path: HEXO_PATH.siteConfig,
53 | raw_content: getFile(HEXO_PATH.siteConfig)
54 | }
55 | };
56 |
57 | getMdFiles(HEXO_PATH.postPath, null, function (dataArr) {
58 | siteData.posts = dataArr;
59 | done();
60 | });
61 | getMdFiles(HEXO_PATH.draftPath, null, function (dataArr) {
62 | siteData.drafts = dataArr;
63 | done();
64 | });
65 | getMdFiles(HEXO_PATH.trashPath, null, function (dataArr) {
66 | siteData.trash = dataArr;
67 | done();
68 | });
69 | getMdFiles(HEXO_PATH.sourcePath, {
70 | match: /index\.md$/,
71 | excludeDir: ['_drafts', '_posts', '_trash']
72 | }, function (dataArr) {
73 | siteData.pages = dataArr;
74 | done();
75 | });
76 |
77 | function done() {
78 | if (++loadingProcess === 4) {
79 | siteData.tags = getTagData(siteData.posts);
80 | FS.writeFile(
81 | PATH.join(HEXO_PATH.adminPath, '__siteDB.json'),
82 | JSON.stringify(siteData),
83 | function (err) {
84 | if (err) throw err;
85 | LOGGER.info('__siteDB.json update!');
86 | }
87 | );
88 | }
89 | }
90 | }
91 | };
92 |
93 | /**
94 | * 读取 markdown 文件
95 | * @param {string} dirPath - 文件路径
96 | * @param {object} match - 匹配文件
97 | * @param {function} callback
98 | * @returns {object[]} array of file content
99 | * */
100 | function getMdFiles(dirPath, match, callback) {
101 | let data = [];
102 |
103 | DIR.readFiles(dirPath, match || {match: /.md$/},
104 | function (err, content, filename, next) {
105 | if (err) throw err;
106 | data.push(EXTEND(parseFileContent(filename, content), {page_url: filename.replace(/^.*source\//g, '')}));
107 | next();
108 | },
109 | function (err, files) {
110 | if (err) throw err;
111 | callback(data.sort(sortListFromNewToOld));
112 | });
113 | }
114 |
115 | /**
116 | * 读取文件内容
117 | * @param {string} filePath 文件路径
118 | * @returns {string} fileContent
119 | * */
120 | function getFile(filePath) {
121 | var fileContent;
122 |
123 | try {
124 | fileContent = FS.readFileSync(filePath, 'utf-8');
125 | }
126 | catch (e) {
127 | throw e;
128 | }
129 |
130 | return fileContent;
131 | }
132 |
133 | /**
134 | * 对文章内容进行整理
135 | * @param fileNameWithPath {string} file name with path
136 | * @param content {string} file content
137 | * */
138 | function parseFileContent(fileNameWithPath, content) {
139 | let file = {
140 | file_name: fileNameWithPath.replace(/^.*\//g, ''),
141 | file_path: fileNameWithPath,
142 | raw_content: content
143 | };
144 | EXTEND(file, FRONT_MATTER(content));
145 | // todo 如果没有时间,则设置文件创建时间
146 | file.date_unix = Date.parse(file.date) || 0;
147 | return file;
148 | }
149 |
150 | /**
151 | * 从文件数组中统计tags
152 | * @param {object[]} fileArr 文件数组
153 | * @returns {object} tag统计对象
154 | * */
155 | function getTagData(fileArr) {
156 | var tagData = {length: 0};
157 |
158 | fileArr.forEach(function (item) {
159 | if (!item.hasOwnProperty('tags') || !item.tags) return;
160 | Array.isArray(item.tags) ?
161 | item.tags.forEach(addNewTag) :
162 | addNewTag(item.tags);
163 | });
164 |
165 | function addNewTag(tagName) {
166 | if (tagData.hasOwnProperty(tagName)) {
167 | tagData[tagName].length++;
168 | }
169 | else {
170 | tagData[tagName] = {length: 1};
171 | tagData.length++;
172 | }
173 | }
174 |
175 | return tagData;
176 | }
177 |
178 | /** 对文件数组进行排序 */
179 | function sortListFromNewToOld(file1, file2) {
180 | if (file1.date_unix > file2.date_unix) {
181 | return -1;
182 | }
183 | else if (file1.date_unix < file2.date_unix) {
184 | return 1;
185 | }
186 | else {
187 | return 0;
188 | }
189 | }
--------------------------------------------------------------------------------
/views/scss/style.scss:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 |
3 | *:focus,
4 | *:active {
5 | outline: none !important;
6 | }
7 |
8 | //todo add autoprefixer
9 | pre {
10 | white-space: pre-line;
11 | }
12 |
13 | @media (min-width: 768px) {
14 | #content-detail-wrap,
15 | header,
16 | main,
17 | footer {
18 | padding-left: calc(100vw - 100%);
19 | }
20 | }
21 |
22 | header {
23 | h1 a {
24 | color: inherit;
25 |
26 | &:hover {
27 | color: inherit;
28 | text-decoration: none;
29 | }
30 | }
31 | }
32 |
33 | footer {
34 | padding-top: 50px;
35 | padding-bottom: 10px;
36 | font-size: 12px;
37 | color: #ccc;
38 | }
39 |
40 | .no-scroll {
41 | overflow: hidden;
42 | }
43 |
44 | .panel-body button {
45 | margin: 3px 2px;
46 | }
47 |
48 | .form-control {
49 | border-radius: 0;
50 | box-shadow: none;
51 | border-color: #eee;
52 | padding-left: 8px;
53 | padding-right: 8px;
54 |
55 | &:focus,
56 | &:active {
57 | border-color: #eee;
58 | box-shadow: none;
59 | }
60 | }
61 |
62 | #list-nav {
63 | margin-bottom: 20px;
64 |
65 | ~ [data-type] {
66 | display: none;
67 | }
68 |
69 | @each $type in posts, pages, drafts, trash {
70 | &[data-select="#{$type}"] {
71 | li[data-type="#{$type}"] a {
72 | color: #555;
73 | background-color: #fff;
74 | border: 1px solid #ddd;
75 | border-bottom-color: transparent;
76 | cursor: default;
77 | }
78 |
79 | ~ [data-type="#{$type}"] {
80 | display: block;
81 | }
82 | }
83 | }
84 | }
85 |
86 | .list-action li {
87 | cursor: pointer;
88 | }
89 |
90 | .f-item {
91 | $item-transition-time: 0.4s;
92 |
93 | position: relative;
94 | padding: 15px 20px;
95 | overflow: hidden;
96 | transition: all $item-transition-time;
97 | cursor: pointer;
98 |
99 | &:hover {
100 | background-color: #f1f1f1;
101 |
102 | .delete {
103 | opacity: 1;
104 | visibility: visible;
105 | }
106 | }
107 |
108 | h4 {
109 | max-height: 50px;
110 | overflow: hidden;
111 | margin-top: 5px;
112 | margin-bottom: 8px;
113 | line-height: 1.4;
114 | }
115 |
116 | time,
117 | span,
118 | small {
119 | color: #bbb;
120 | font-size: 12px;
121 | vertical-align: text-top;
122 | }
123 |
124 | .tags {
125 | margin-top: 2px;
126 | }
127 |
128 | .delete {
129 | position: absolute;
130 | top: 50%;
131 | right: 10px;
132 | z-index: 10;
133 | opacity: 0;
134 | visibility: hidden;
135 | transform: translate(0, -50%);
136 | transition: all $item-transition-time;
137 | }
138 |
139 | &[data-type="pages"] h4::before {
140 | content: "/";
141 | }
142 | }
143 |
144 | #content-detail-wrap {
145 | position: fixed;
146 | top: 0;
147 | left: 0;
148 | z-index: 100;
149 | width: 100%;
150 | height: 100%;
151 | overflow-y: scroll;
152 | background-color: rgba(255, 255, 255, 0.5);
153 | visibility: hidden;
154 | opacity: 0;
155 | transition: all .4s;
156 |
157 | &.show {
158 | visibility: visible;
159 | opacity: 1;
160 |
161 | #content-detail-inner {
162 | top: 0;
163 | }
164 |
165 | ~ header,
166 | ~ main,
167 | ~ footer {
168 | -webkit-filter: blur(5px);
169 | filter: blur(5px);
170 | }
171 | }
172 | }
173 |
174 | #content-detail-inner {
175 | position: relative;
176 | top: -50px;
177 | margin: 50px 20px;
178 | max-width: 700px;
179 | padding-top: 20px;
180 | padding-bottom: 20px;
181 | min-height: calc(100vh - 100px);
182 | background-color: #ffffff;
183 | //box-shadow: 0 0 1px #eee;
184 | border: 1px solid #eee;
185 | transition: all .5s;
186 |
187 | @media (min-width: 768px) {
188 | width: 70%;
189 | margin-left: auto;
190 | margin-right: auto;
191 | padding-top: 50px;
192 | padding-bottom: 50px;
193 | }
194 |
195 | > div {
196 | padding-left: 20px;
197 | padding-right: 20px;
198 |
199 | @media (min-width: 768px) {
200 | padding-left: 40px;
201 | padding-right: 40px;
202 | }
203 | }
204 |
205 | &.processing {
206 | &::before,
207 | #content-detail-body {
208 | -webkit-filter: blur(4px);
209 | filter: blur(4px);
210 | }
211 |
212 | &::after {
213 | content: 'Sending content...';
214 | display: block;
215 | position: absolute;
216 | top: 0;
217 | left: 0;
218 | z-index: 10;
219 | width: 100%;
220 | height: 100%;
221 | text-align: center;
222 | line-height: 100vh;
223 | }
224 | }
225 |
226 | &[data-type]::before {
227 | content: "";
228 | position: absolute;
229 | top: 20px;
230 | left: -1px;
231 | z-index: 10;
232 | padding: 5px 12px;
233 | border: 1px solid #eee;
234 | background-color: #f1f1f1;
235 | }
236 |
237 | &[data-type="posts"]::before {
238 | content: "Post";
239 | }
240 |
241 | &[data-type="pages"]::before {
242 | content: "Page";
243 | }
244 |
245 | &[data-type="drafts"]::before {
246 | content: "Draft";
247 | }
248 |
249 | &[data-type="trash"]::before {
250 | content: "Trash";
251 | }
252 |
253 | &[data-type="new"]::before {
254 | content: "New";
255 | }
256 | }
257 |
258 | #content-detail {
259 | white-space: pre-line;
260 | }
261 |
262 | #content-detail-body {
263 | position: relative;
264 | margin-top: 20px;
265 |
266 | textarea {
267 | display: block;
268 | width: 100%;
269 | min-height: 50vh;
270 | margin-top: 10px;
271 | padding: 8px;
272 | border: 1px solid #eee;
273 | resize: vertical;
274 | line-height: 1.4;
275 | }
276 | }
277 |
278 | #content-tool {
279 | margin-top: 50px;
280 | transition: all .4s;
281 |
282 | @media (min-width: 768px) {
283 | position: fixed;
284 | left: 80%;
285 | top: 20%;
286 | margin-top: 0;
287 | z-index: -1;
288 | }
289 |
290 | @media (min-width: 1000px) {
291 | left: calc(50% + 300px);
292 | }
293 |
294 | li {
295 | position: relative;
296 | display: inline-block;
297 | margin: 5px;
298 | padding: 8px 12px;
299 | border: 1px solid #eee;
300 | background-color: #f1f1f1;
301 | transition: all .4s ease-in-out;
302 |
303 | @media (min-width: 768px) {
304 | float: left;
305 | clear: left;
306 | padding-left: 50px;
307 | }
308 |
309 | //&:hover {
310 | // //background-color: #f1f1f1;
311 | // color: #337ab7;
312 | //}
313 |
314 | &.collapse {
315 | transform: translateX(-100%);
316 | }
317 | }
318 | }
319 |
320 | #t-save {
321 | &.processing {
322 | color: transparent;
323 |
324 | &::after {
325 | display: block;
326 | content: "";
327 | width: 20px;
328 | height: 20px;
329 | position: absolute;
330 | top: 50%;
331 | right: 12px;
332 | border-radius: 50%;
333 | border: 2px solid #999;
334 | border-top-color: transparent;
335 | transform: translateY(-50%);
336 | animation: spin 1s linear infinite;
337 | }
338 | }
339 | }
340 |
341 | #close-detail {
342 | position: absolute;
343 | right: 10px;
344 | top: 5px;
345 | z-index: 20;
346 | padding: 10px;
347 | line-height: 1;
348 | font-size: 20px;
349 | cursor: pointer;
350 | }
351 |
352 | #alert {
353 | max-width: 80%;
354 | position: fixed;
355 | top: 0;
356 | left: 50%;
357 | z-index: 999;
358 | padding-top: 10px;
359 | padding-bottom: 10px;
360 | transition: all .4s;
361 | transform: translate(-50%, -100%);
362 | word-break: break-all;
363 |
364 | &.show {
365 | transform: translate(-50%, 0);
366 | }
367 |
368 | @media (min-width: 768px) {
369 | max-width: 400px;
370 | }
371 | }
372 |
373 | #hexo-actions {
374 | #visit-hexo,
375 | #cli-hexo-stop {
376 | display: none;
377 | }
378 |
379 | &.running {
380 | #visit-hexo {
381 | display: block;
382 | }
383 |
384 | #cli-hexo-stop {
385 | display: inline-block;
386 | }
387 |
388 | #cli-hexo-server {
389 | display: none;
390 | }
391 | }
392 | }
393 |
394 | #visit-hexo {
395 | margin-bottom: 10px;
396 | padding: 0 6px;
397 | }
398 |
399 | @keyframes spin {
400 | 0% {
401 | transform: translateY(-50%) rotate(0deg);
402 | }
403 |
404 | 50% {
405 | transform: translateY(-50%) rotate(180deg);
406 | }
407 |
408 | 100% {
409 | transform: translateY(-50%) rotate(360deg);
410 | }
411 | }
--------------------------------------------------------------------------------
/controller/index.js:
--------------------------------------------------------------------------------
1 | const HEXO_PATH = require('../module/config-init').data(),
2 | PATH = require('path'),
3 | FS = require('fs'),
4 | LOGGER = require('log4js').getLogger(),
5 | GET_ALL_DATA = require('../module/get-all-data');
6 |
7 | exports.entry = function (req, res) {
8 | deleteDBCache();
9 | res.render('index.ejs', {data: require('../__siteDB.json')});
10 | };
11 |
12 | exports.getAll = function (req, res) {
13 | deleteDBCache();
14 | LOGGER.info('get db content');
15 | res.json({status: "success", data: require('../__siteDB.json')});
16 | };
17 |
18 | exports.getMarkdownFile = function (req, res) {
19 | deleteDBCache();
20 | var query = req.query,
21 | DB = require('../__siteDB.json');
22 |
23 | if (query.type && !Number.isNaN(Number(query.index))) {
24 | if (DB[query.type][query.index]) {
25 | LOGGER.info('getting:' + DB[query.type][query.index].file_path);
26 | res.json(DB[query.type][query.index]);
27 | }
28 | else {
29 | LOGGER.error("Didn't find post!");
30 | res.status(500).send({"type": "error", "msg": "Didn't find post!", "param": query});
31 | }
32 | }
33 | else {
34 | LOGGER.error('get markdown file param error! param:' + JSON.stringify(query));
35 | res.status(500).send({"type": "error", "msg": "Param error!", "param": query});
36 | }
37 | };
38 |
39 | exports.getConfig = function (req, res) {
40 | deleteDBCache();
41 | var query = req.query,
42 | DB = require('../__siteDB.json');
43 |
44 | if (query.type == 'site') {
45 | LOGGER.info('getting:' + DB.siteConfig.file_path);
46 | res.json(DB.siteConfig);
47 | }
48 | else if (query.type = 'theme') {
49 | LOGGER.info('getting:' + DB.themeConfig.file_path);
50 | res.json(DB.themeConfig);
51 | }
52 | else {
53 | LOGGER.warn('wrong config type:' + JSON.stringify(query));
54 | res.status(500).send({"msg": "wrong type", "param": query});
55 | }
56 | };
57 |
58 | exports.writeMarkdownFile = function (req, res) {
59 | deleteDBCache();
60 | var body = req.body,
61 | DB = require('../__siteDB.json'),
62 | file_in_DB, file_name, file_path,
63 | file_type = body.type,
64 | file_index = body.index,
65 | file_content = body.content;
66 |
67 | if (body !== undefined && body !== null) {
68 | // 如果有index属性,表示是更新现有的文件,否则表示新增文件
69 | if (file_index) {
70 | file_in_DB = DB[file_type][file_index];
71 | file_name = file_in_DB.file_name;
72 |
73 | file_type === 'pages' ?
74 | file_path = PATH.join(HEXO_PATH.sourcePath, file_in_DB.page_url, file_name) :
75 | file_path = PATH.join(HEXO_PATH.sourcePath, '_' + file_type, file_name);
76 |
77 | writeFile(file_path);
78 | }
79 | else {
80 | file_name = body.file_name;
81 |
82 | if (file_type === 'pages') {
83 | file_path = PATH.join(HEXO_PATH.sourcePath, file_name);
84 |
85 | FS.access(file_path, FS.F_OK, function (err) {
86 | if (err) {
87 | try {
88 | FS.mkdirSync(file_path);
89 | file_path = PATH.join(file_path, 'index.md');
90 | writeFile(file_path);
91 | }
92 | catch (e) {
93 | throw 'create PATH failed:' + file_path;
94 | }
95 | }
96 | else {
97 | LOGGER.warn('page path is already exist:source/' + file_name);
98 | res.status(500).send({status: 'error', msg: 'page path is already exist:source/' + file_name});
99 | }
100 | });
101 | }
102 |
103 | else if (file_type === 'posts' || file_type === 'drafts') {
104 | file_path = PATH.join(HEXO_PATH[file_type.replace(/s$/, '') + 'Path'], file_name + '.md');
105 |
106 | FS.access(file_path, FS.F_OK, function (err) {
107 | if (err) {
108 | writeFile(file_path);
109 | }
110 | else {
111 | LOGGER.warn('file is already exist:' + file_name);
112 | res.status(500).send({status: 'error', msg: 'file is already exist:' + file_name + '.md'});
113 | }
114 | });
115 | }
116 |
117 | else {
118 | LOGGER.error('wrong post type');
119 | res.status(500).send({status: 'error', msg: 'Wrong type!'});
120 | }
121 | }
122 |
123 | function writeFile(filePath) {
124 | FS.writeFile(filePath, file_content, 'utf-8', function (err) {
125 | if (err) {
126 | LOGGER.error('write file:' + filePath + ' error! error:' + JSON.stringify(err));
127 | res.status(500).send({status: 'error', msg: 'file write err!'});
128 | }
129 |
130 | LOGGER.info('write file:' + filePath + ' succeed');
131 | GET_ALL_DATA.updateDBFile();
132 | res.json({status: 'success'});
133 | });
134 | }
135 | }
136 | else {
137 | LOGGER.error('didn\'t receive param');
138 | res.status(500).send({status: 'error', msg: 'Didn\'t receive param'});
139 | }
140 | };
141 |
142 | exports.moveMarkdownFile = function (req, res) {
143 | deleteDBCache();
144 | var body = req.body,
145 | type = body.type,
146 | index = body.index,
147 | target_type = body.target_type.replace(/s$/, ''),
148 | DB = require('../__siteDB.json');
149 |
150 | try {
151 | var file = DB[type][index];
152 | }
153 | catch (e) {
154 | LOGGER.error('didn\'t find post!');
155 | res.status(500).send({"status": "error", "msg": "Didn't find post!"});
156 | throw e;
157 | }
158 |
159 | var file_name, oldPath, newPath;
160 |
161 | switch (type) {
162 | case 'pages':
163 | file_name = file.page_url + '.md';
164 | oldPath = PATH.join(HEXO_PATH.sourcePath, file.page_url, 'index.md');
165 | newPath = PATH.join(HEXO_PATH[target_type + 'Path'], file_name);
166 |
167 | FS.access(
168 | newPath,
169 | FS.F_OK,
170 | function (err) {
171 | if (err) {
172 | moveFile(
173 | oldPath,
174 | newPath,
175 |
176 | // 移动完成后,需要删除原来的空文件夹
177 | function () {
178 | FS.rmdir(
179 | PATH.join(HEXO_PATH.sourcePath, file.page_url),
180 | function (err) {
181 | if (err) {
182 | LOGGER.warn('can\'t rmdir:' + PATH.join(HEXO_PATH.sourcePath, file.page_url));
183 | throw err;
184 | }
185 | LOGGER.info('rmdir:' + PATH.join(HEXO_PATH.sourcePath, file.page_url));
186 | }
187 | );
188 | }
189 | );
190 | }
191 | else {
192 | LOGGER.error(newPath + " already exist!");
193 | res.status(500).send({"status": "error", "msg": newPath + " already exist!"});
194 | }
195 | }
196 | );
197 | break;
198 | case 'posts':
199 | case 'drafts':
200 | case 'trash':
201 | file_name = file.file_name;
202 | oldPath = PATH.join(HEXO_PATH[type.replace(/s$/, '') + 'Path'], file_name);
203 |
204 | if (type === 'trash' && target_type === 'trash') {
205 | FS.unlink(
206 | oldPath,
207 | function (err) {
208 | if (err) {
209 | LOGGER.error('failed to delete file:' + oldPath);
210 | res.status(500).send({"status": "error", "msg": "failed to delete file:" + oldPath});
211 | throw err;
212 | }
213 |
214 | LOGGER.info('delete:' + oldPath + ' succeed');
215 | GET_ALL_DATA.updateDBFile();
216 | res.json({"status": "success", "msg": "delete:" + oldPath + " succeed"});
217 | }
218 | )
219 | }
220 | else {
221 | if (target_type === 'page') {
222 | newPath = PATH.join(HEXO_PATH.sourcePath, file_name.replace(/\.md$/, ''));
223 |
224 | FS.access(
225 | newPath,
226 | FS.F_OK,
227 | function (err) {
228 | if (err) {
229 | FS.mkdir(
230 | newPath,
231 | function (err) {
232 | if (err) throw err;
233 | moveFile(oldPath, PATH.join(newPath, 'index.md'));
234 | }
235 | )
236 | }
237 | else {
238 | LOGGER.error(newPath + " already exist!");
239 | res.status(500).send({"status": "error", "msg": newPath + " already exist!"});
240 | }
241 | }
242 | );
243 | }
244 | else {
245 | newPath = PATH.join(HEXO_PATH[target_type + 'Path'], file_name);
246 |
247 | FS.access(
248 | newPath,
249 | FS.F_OK,
250 | function (err) {
251 | if (err) {
252 | moveFile(oldPath, newPath);
253 | }
254 | else {
255 | LOGGER.error(newPath + " already exist!");
256 | res.status(500).send({"status": "error", "msg": newPath + " already exist!"});
257 | }
258 | }
259 | );
260 | }
261 | }
262 | break;
263 | default:
264 | LOGGER.error("option doesn't support!param:" + JSON.stringify(body));
265 | res.status(500).send({"status": "error", "msg": "option doesn't support!param:" + JSON.stringify(body)});
266 | }
267 |
268 | function moveFile(oldPath, newPath, fn) {
269 | FS.rename(
270 | oldPath,
271 | newPath,
272 | function (err) {
273 | if (err) {
274 | LOGGER.error('Failed to move file:' + oldPath + ' to ' + newPath);
275 | res.status(500).send({"status": "error", "msg": "Move file failed"});
276 | throw err;
277 | }
278 |
279 | if (fn && typeof fn === 'function') fn();
280 |
281 | LOGGER.info('Move file:' + oldPath + ' to ' + newPath);
282 | GET_ALL_DATA.updateDBFile();
283 | res.json({"status": "success"});
284 | }
285 | );
286 | }
287 | };
288 |
289 | function deleteDBCache() {
290 | delete require.cache[PATH.join(HEXO_PATH.adminPath, '__siteDB.json')];
291 | }
--------------------------------------------------------------------------------
/views/public/js/entry.min.js:
--------------------------------------------------------------------------------
1 | !function(e){function t(a){if(n[a])return n[a].exports;var s=n[a]={exports:{},id:a,loaded:!1};return e[a].call(s.exports,s,s.exports,t),s.loaded=!0,s.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){function a(e){var t=e.tpl||n(1);$("#content-detail-inner").attr("data-type",e.type),$("#content-detail-body").empty().html(t({data:e})),$("#content-detail-wrap").addClass("show"),$("html, body").addClass("no-scroll")}function s(e){var t=$("#content-tool"),n=t.attr("data-type"),a=t.attr("data-index");return n&&a?void $.ajax({url:"/move-markdown-file",method:"post",timeout:d,data:{type:n,index:a,target_type:e},dataType:"json",cache:!1,success:function(t){t.status&&"success"==t.status?($("#close-detail").trigger("click"),i("success","Success!"),o(e)):i("warning","moveFileTo unknown problem!")},error:function(e){i("danger","Error:"+e.responseJSON.msg)}}):void i("danger","Param error!")}function o(e){e=e||"posts",$.ajax({url:"/get-all",method:"get",timeout:d,dataType:"json",cache:!1,success:function(t){if(t.status&&"success"==t.status){t=t.data,$("#list-nav").parent().html(n(2)({data:t,type:e}));var a="",s=$("#tag-list");for(var o in t.tags)t.tags.hasOwnProperty(o)&&"length"!==o&&(a+=""+o+" ( "+t.tags[o].length+" )");return s.find("h3").text(t.tags.length+" tags"),void s.find("ul").html(a)}i("warning","refreshList unknown problem!")},error:function(e){i("danger","Error:"+e.responseJSON)}})}function i(e,t){function n(){a.removeClass("alert-success alert-info alert-danger alert-warning"),a.addClass("alert-"+e+" show").html(t)}var a=$("#alert");a.hasClass("show")?(a.removeClass("show"),setTimeout(n,400)):n(),r&&clearTimeout(r),r=setTimeout(function(){a.removeClass("show")},3e3)}function p(e){return 0===e.replace(/[a-zA-Z0-9]|_|-/g,"").length}const d=3e4;"1"===sessionStorage.getItem("hexo-status")&&$("#hexo-actions").addClass("running"),$.fn.multiOn=function(e){for(var t in e)if(e.hasOwnProperty(t))for(var n in e[t])e[t].hasOwnProperty(n)&&$(this).on(t,n,e[t][n])},$(".view-config").click(function(){var e=$(this).attr("data-type");return e.search(/site|theme/)===-1?void console.warn("no such config type:"+e):void $.ajax({url:"/get-config",method:"get",timeout:d,data:{type:e},dataType:"json",cache:!1,success:a,error:function(e){console.log("error, err=%O",e.responseJSON)}})}),$("#close-detail").click(function(){$("#content-detail-inner").removeAttr("data-type class"),$("#content-detail-wrap").removeClass("show"),$("html, body").removeClass("no-scroll"),setTimeout(function(){$("#content-detail-inner").children("div").empty()},400)}),$("body").mousedown(function(e){var t=$("#content-detail-inner");$("#content-detail-wrap").hasClass("show")&&!t.is(e.target)&&0===t.has(e.target).length&&$("#close-detail").trigger("click")}),$("#alert").click(function(){$(this).removeClass("show")}),$("#main-container").multiOn({click:{".f-item":function(){var e=$(this),t=e.attr("data-type"),n=e.attr("data-index");$.ajax({url:"/get-markdown-file",method:"get",timeout:d,data:{type:t,index:n},dataType:"json",cache:!1,success:function(e){a($.extend(e,{type:t,index:n}))},error:function(e){console.log("error, err=%O",e.responseJSON.msg)}})},"#list-nav li":function(){$(this).parent().attr("data-select",$(this).attr("data-type"))},"#cli-hexo-server":function(){var e=$(this);e.attr("disabled","disabled"),$.ajax({url:"/hexo-server",method:"get",timeout:d,dataType:"json",cache:!1,success:function(t){t&&t.status&&"success"==t.status?(i("success","hexo launch success! Visit:localhost:4000"),$("#hexo-actions").addClass("running"),sessionStorage.setItem("hexo-status","1")):i("failed","hexo launch failed! Unknown!"),e.removeAttr("disabled")},error:function(t){i("danger","hexo launch failed!=err="+JSON.stringify(t)),e.removeAttr("disabled")}})},"#cli-hexo-stop":function(){var e=$(this);e.attr("disabled","disabled"),$.ajax({url:"/hexo-kill",method:"get",timeout:d,dataType:"json",cache:!1,success:function(t){t&&t.status&&"success"==t.status?(i("success","hexo killed!"),$("#hexo-actions").removeClass("running"),sessionStorage.setItem("hexo-status","0")):i("failed","hexo failed to kill! Unknown!"),e.removeAttr("disabled")},error:function(){i("success","hexo killed!"),e.removeAttr("disabled"),$("#hexo-actions").removeClass("running"),sessionStorage.removeItem("hexo-status")}})},"#cli-hexo-deploy":function(){var e=$(this);e.attr("disabled","disabled"),$.ajax({url:"/hexo-deploy",method:"get",timeout:d,dataType:"json",cache:!1,success:function(t){t&&t.status&&"success"==t.status?i("success","hexo deployed!"):i("failed","hexo failed to deploy! Unknown!"),e.removeAttr("disabled")},error:function(t){i("danger","hexo failed to deploy! err="+JSON.stringify(t)),e.removeAttr("disabled")}})}}}),$("#content-detail-body").multiOn({click:{"#t-edit":function(){var e=$(this),t=e.parent().children("li");t.addClass("collapse"),setTimeout(function(){e.attr("id","t-save").text("Save"),$('Cancel').insertAfter(e),e.removeClass("collapse"),setTimeout(function(){$("#t-cancel").removeClass("collapse")},100)},400);var n=$("#content-detail"),a=n.text();sessionStorage.setItem("content-cache",a),n.empty().html('")},"#t-cancel":function(){var e=$("#content-detail"),t=$(this),n=t.parent().children("li"),a=sessionStorage.getItem("content-cache");e.html(a),n.addClass("collapse"),setTimeout(function(){$("#t-save").attr("id","t-edit").text("Edit"),t.remove(),n.removeAttr("class")},400)},"#t-save":function(){var e=$(this),t=$("#content-editor"),n=$("#content-tool"),a=$("#content-detail"),s=n.attr("data-type"),p=n.attr("data-index");e.hasClass("processing")||(e.addClass("processing"),$.ajax({url:"/write-markdown-file",method:"post",timeout:d,data:{type:s,index:p,content:t.val()},dataType:"json",cache:!1,success:function(p){p.status&&"success"==p.status?(n.children("li").addClass("collapse"),a.text(t.val().trim()),setTimeout(function(){e.attr("id","t-edit").text("Edit"),$("#t-cancel").remove(),n.children("li").removeAttr("class"),i("success","Saved!"),o(s)},400)):(e.removeClass("processing"),i("warning","Unknown problem!"))},error:function(t){e.removeClass("processing"),i("danger","Error:"+t.responseJSON)}}))},"#t-publish-new":function(){var e=$("#content-detail-inner"),t=$("#t-publish-new"),n=$("#new-filename").val().trim(),a=$('[name="new-type"]:checked').val().trim(),s=$("#content-editor").val();return e.addClass("processing"),t.attr("disabled","disabled"),n&&p(n)?void $.ajax({url:"/write-markdown-file",method:"post",timeout:d,data:{type:a,file_name:n,content:s},dataType:"json",cache:!1,success:function(e){e.status&&"success"==e.status?($("#close-detail").trigger("click"),i("success","Succeed!"),o(a)):i("warning","Unknown problem!")},error:function(n){i("danger","Error:"+n.responseJSON.msg),e.removeClass("processing"),t.removeAttr("disabled")}}):(i("danger","File name/page PATH wrong!"),t.removeAttr("disabled"),e.removeClass("processing"),!1)},"#t-unpublish":function(){s("drafts")},"#t-delete":function(){s("trash")},"#t-publish-post":function(){s("posts")},"#t-publish-page":function(){s("pages")}}}),$("#add-new").click(function(){a({type:"new",tpl:n(3)})});var r},function(module,exports){module.exports=function anonymous(locals,escape,include,rethrow){function encode_char(e){return _ENCODE_HTML_RULES[e]||e}escape=escape||function(e){return void 0==e?"":String(e).replace(_MATCH_HTML,encode_char)};var _ENCODE_HTML_RULES={"&":"&","<":"<",">":">",'"':""","'":"'"},_MATCH_HTML=/[&<>'"]/g,__output=[],__append=__output.push.bind(__output);with(locals||{}){data.type&&(__append('\n\n - Edit
\n\n '),"posts"==data.type||"pages"==data.type?__append('\n - Move to draft
\n '):"drafts"==data.type?__append('\n - Publish to post
\n - Publish to page
\n '):"trash"==data.type&&__append('\n - Restore to post
\n - Restore to page
\n - Restore to draft
\n '),__append("\n\n "),__append("trash"===data.type?'\n - Delete
\n ':'\n - Move to trash
\n '),__append("\n
\n")),__append("\n");var data_content="no content";data.raw_content?data_content=data.raw_content.trim():"string"==typeof data&&(data_content=data.trim()),__append("\n"),data.page_url?(__append("\n/"),__append(escape(data.page_url)),__append("
\n")):data.title&&(__append("\n"),__append(escape(data.title)),__append("
\n")),__append("\n\n"),data.file_path&&(__append('\n'),__append(escape(data.file_path)),__append("\n")),__append('\n\n
\n\n'),__append(escape(data_content)),__append("
")}return __output.join("")}},function(module,exports){module.exports=function anonymous(locals,escape,include,rethrow){function encode_char(e){return _ENCODE_HTML_RULES[e]||e}escape=escape||function(e){return void 0==e?"":String(e).replace(_MATCH_HTML,encode_char)};var _ENCODE_HTML_RULES={"&":"&","<":"<",">":">",'"':""","'":"'"},_MATCH_HTML=/[&<>'"]/g,__output=[],__append=__output.push.bind(__output);with(locals||{}){if("undefined"==typeof type)var type="posts";__append('\n\n\n");var contentType=["posts","pages","drafts","trash"];__append("\n");for(var i=0;i\n '),data[contentType[i]].length>0?(__append("\n\n "),data[contentType[i]].forEach(function(e,t){__append('\n \n
\n\n '),e.file_name&&(__append('\n
\n \n '),__append(escape(e.file_name)),__append("\n
\n ")),__append("\n\n "),"pages"===contentType[i]?(__append("\n "),e.page_url&&(__append("\n
"),__append(escape(e.page_url)),__append("
\n ")),__append("\n ")):(__append("\n "),e.title&&(__append("\n
"),__append(escape(e.title)),__append("
\n ")),__append("\n ")),__append("\n\n "),e.date&&(__append('\n
\n ")),__append("\n\n "),e.tags&&(__append('\n
\n \n '),Array.isArray(e.tags)?(__append("\n "),e.tags.forEach(function(e){__append("\n #"),__append(escape(e)),__append("\n ")}),__append("\n ")):(__append("\n #"),__append(escape(e.tags)),__append("\n ")),__append("\n
\n ")),__append("\n
\n
\n ")}),__append("\n\n ")):__append('\n\n \n\n '),__append("\n\n")}return __output.join("")}},function(module,exports){module.exports=function anonymous(locals,escape,include,rethrow){function encode_char(e){return _ENCODE_HTML_RULES[e]||e}escape=escape||function(e){return void 0==e?"":String(e).replace(_MATCH_HTML,encode_char)};var _ENCODE_HTML_RULES={"&":"&","<":"<",">":">",'"':""","'":"'"},_MATCH_HTML=/[&<>'"]/g,__output=[],__append=__output.push.bind(__output);with(locals||{}){__append('\n\n
\n\n\n
\n\n\n\n\n
\n\n\n');var time=new Date;time=time.getFullYear()+"-"+(time.getMonth()+1)+"-"+time.getDate()+" "+(1===time.getHours().toString().length?"0"+time.getHours():time.getHours())+":"+(1===time.getMinutes().toString().length?"0"+time.getMinutes():time.getMinutes())+":"+(1===time.getSeconds().toString().length?"0"+time.getSeconds():time.getSeconds()),__append('\n\n\n\n
\n\n\n \n
\n')}return __output.join("")}}]);
--------------------------------------------------------------------------------
/views/js/entry.js:
--------------------------------------------------------------------------------
1 | const AJAX_TIMEOUT = 1000 * 30;
2 |
3 | if (sessionStorage.getItem('hexo-status') === '1') {
4 | $('#hexo-actions').addClass('running');
5 | }
6 |
7 | // 事件委托
8 | $.fn.multiOn = function (obj) {
9 | for (var eName in obj)
10 | if (obj.hasOwnProperty(eName))
11 | for (var selector in obj[eName])
12 | if (obj[eName].hasOwnProperty(selector))
13 | $(this).on(eName, selector, obj[eName][selector]);
14 | };
15 |
16 | $('.view-config').click(function () {
17 | var type = $(this).attr('data-type');
18 |
19 | if (type.search(/site|theme/) === -1) {
20 | console.warn('no such config type:' + type);
21 | return;
22 | }
23 |
24 | $.ajax({
25 | url: '/get-config',
26 | method: 'get',
27 | timeout: AJAX_TIMEOUT,
28 | data: {type: type},
29 | dataType: 'json',
30 | cache: false,
31 | success: showContentDetail,
32 | error: function (err) {
33 | console.log('error, err=%O', err.responseJSON);
34 | }
35 | });
36 | });
37 |
38 | $('#close-detail').click(function () {
39 | $('#content-detail-inner').removeAttr('data-type class');
40 | $('#content-detail-wrap').removeClass('show');
41 | $('html, body').removeClass('no-scroll');
42 | setTimeout(function () {
43 | $('#content-detail-inner').children('div').empty();
44 | }, 400);
45 | });
46 |
47 | $('body').mousedown(function (e) {
48 | var $target = $('#content-detail-inner');
49 | if ($('#content-detail-wrap').hasClass('show') && !$target.is(e.target) && $target.has(e.target).length === 0) {
50 | $('#close-detail').trigger('click');
51 | }
52 | });
53 |
54 | $('#alert').click(function () {
55 | $(this).removeClass('show');
56 | });
57 |
58 | $('#main-container').multiOn({
59 | click: {
60 | '.f-item': function () {
61 | var $this = $(this),
62 | type = $this.attr('data-type'),
63 | index = $this.attr('data-index');
64 |
65 | $.ajax({
66 | url: '/get-markdown-file',
67 | method: 'get',
68 | timeout: AJAX_TIMEOUT,
69 | data: {
70 | type: type,
71 | index: index
72 | },
73 | dataType: 'json',
74 | cache: false,
75 | success: function (data) {
76 | showContentDetail($.extend(data, {type: type, index: index}))
77 | },
78 | error: function (err) {
79 | console.log('error, err=%O', err.responseJSON.msg);
80 | }
81 | });
82 | },
83 | '#list-nav li': function () {
84 | $(this).parent().attr('data-select', $(this).attr('data-type'));
85 | },
86 | '#cli-hexo-server': function () {
87 | var $this = $(this);
88 | $this.attr('disabled', 'disabled');
89 |
90 | $.ajax({
91 | url: '/hexo-server',
92 | method: 'get',
93 | timeout: AJAX_TIMEOUT,
94 | dataType: 'json',
95 | cache: false,
96 | success: function (data) {
97 | if (data && data.status && data.status == 'success') {
98 | bsAlert('success', 'hexo launch success! Visit:localhost:4000');
99 | $('#hexo-actions').addClass('running');
100 | sessionStorage.setItem('hexo-status', '1');
101 | } else {
102 | bsAlert('failed', 'hexo launch failed! Unknown!')
103 | }
104 | $this.removeAttr('disabled');
105 | },
106 | error: function (err) {
107 | bsAlert('danger', 'hexo launch failed!=err=' + JSON.stringify(err));
108 | $this.removeAttr('disabled');
109 | }
110 | });
111 | },
112 | '#cli-hexo-stop': function () {
113 | var $this = $(this);
114 | $this.attr('disabled', 'disabled');
115 | $.ajax({
116 | url: '/hexo-kill',
117 | method: 'get',
118 | timeout: AJAX_TIMEOUT,
119 | dataType: 'json',
120 | cache: false,
121 | success: function (data) {
122 | if (data && data.status && data.status == 'success') {
123 | bsAlert('success', 'hexo killed!');
124 | $('#hexo-actions').removeClass('running');
125 | sessionStorage.setItem('hexo-status', '0');
126 | } else {
127 | bsAlert('failed', 'hexo failed to kill! Unknown!')
128 | }
129 | $this.removeAttr('disabled');
130 | },
131 | error: function () {
132 | bsAlert('success', 'hexo killed!');
133 | // bsAlert('danger', 'hexo failed to kill! err=' + JSON.stringify(err));
134 | $this.removeAttr('disabled');
135 | $('#hexo-actions').removeClass('running');
136 | sessionStorage.removeItem('hexo-status');
137 | }
138 | });
139 | },
140 | '#cli-hexo-deploy': function () {
141 | var $this = $(this);
142 | $this.attr('disabled', 'disabled');
143 | $.ajax({
144 | url: '/hexo-deploy',
145 | method: 'get',
146 | timeout: AJAX_TIMEOUT,
147 | dataType: 'json',
148 | cache: false,
149 | success: function (data) {
150 | if (data && data.status && data.status == 'success') {
151 | bsAlert('success', 'hexo deployed!');
152 | } else {
153 | bsAlert('failed', 'hexo failed to deploy! Unknown!')
154 | }
155 | $this.removeAttr('disabled');
156 | },
157 | error: function (err) {
158 | bsAlert('danger', 'hexo failed to deploy! err=' + JSON.stringify(err));
159 | $this.removeAttr('disabled');
160 | }
161 | });
162 | }
163 | }
164 | });
165 |
166 | // ------- detail actions ---------
167 | $('#content-detail-body').multiOn({
168 | click: {
169 | // 编辑、取消、保存
170 | '#t-edit': function () {
171 | var $this = $(this),
172 | $tools = $this.parent().children('li');
173 |
174 | // 操作按钮
175 | $tools.addClass('collapse');
176 | setTimeout(function () {
177 | $this.attr('id', 't-save').text('Save');
178 | $('Cancel').insertAfter($this);
179 |
180 | $this.removeClass('collapse');
181 | setTimeout(function () {
182 | $('#t-cancel').removeClass('collapse');
183 | }, 100);
184 | }, 400);
185 |
186 | // 缓存文章内容
187 | var $content = $('#content-detail'),
188 | content = $content.text();
189 |
190 | sessionStorage.setItem('content-cache', content);
191 | $content.empty().html('');
192 | },
193 | '#t-cancel': function () {
194 | var $content = $('#content-detail'),
195 | $this = $(this),
196 | $tools = $this.parent().children('li'),
197 | prevContent = sessionStorage.getItem('content-cache');
198 |
199 | $content.html(prevContent);
200 |
201 | $tools.addClass('collapse');
202 |
203 | setTimeout(function () {
204 | $('#t-save').attr('id', 't-edit').text('Edit');
205 | $this.remove();
206 | $tools.removeAttr('class');
207 | }, 400);
208 | },
209 | '#t-save': function () {
210 | var $this = $(this),
211 | $editor = $('#content-editor'),
212 | $tools = $('#content-tool'),
213 | $content_detail = $('#content-detail'),
214 | type = $tools.attr('data-type'),
215 | index = $tools.attr('data-index');
216 |
217 | if ($this.hasClass('processing')) return;
218 |
219 | $this.addClass('processing');
220 | $.ajax({
221 | url: '/write-markdown-file',
222 | method: 'post',
223 | timeout: AJAX_TIMEOUT,
224 | data: {
225 | 'type': type,
226 | 'index': index,
227 | 'content': $editor.val()
228 | },
229 | dataType: 'json',
230 | cache: false,
231 | success: function (data) {
232 | if (data.status && data.status == 'success') {
233 | $tools.children('li').addClass('collapse');
234 | $content_detail.text($editor.val().trim());
235 |
236 | setTimeout(function () {
237 | $this.attr('id', 't-edit').text('Edit');
238 | $('#t-cancel').remove();
239 | $tools.children('li').removeAttr('class');
240 | bsAlert('success', 'Saved!');
241 | refreshList(type);
242 | }, 400);
243 | }
244 | else {
245 | $this.removeClass('processing');
246 | bsAlert('warning', 'Unknown problem!');
247 | }
248 | },
249 | error: function (err) {
250 | $this.removeClass('processing');
251 | bsAlert('danger', 'Error:' + err.responseJSON);
252 | }
253 | });
254 | },
255 |
256 | // 发布新文章/页面
257 | '#t-publish-new': function () {
258 | var $content_inner = $('#content-detail-inner'),
259 | $publishBtn = $('#t-publish-new'),
260 | file_name = $('#new-filename').val().trim(),
261 | type = $('[name="new-type"]:checked').val().trim(),
262 | content = $('#content-editor').val();
263 |
264 | $content_inner.addClass('processing');
265 | $publishBtn.attr('disabled', 'disabled');
266 |
267 | if (!file_name || !isFilePathNameValid(file_name)) {
268 | bsAlert('danger', 'File name/page PATH wrong!');
269 | $publishBtn.removeAttr('disabled');
270 | $content_inner.removeClass('processing');
271 | return false;
272 | }
273 |
274 | $.ajax({
275 | url: '/write-markdown-file',
276 | method: 'post',
277 | timeout: AJAX_TIMEOUT,
278 | data: {
279 | 'type': type,
280 | 'file_name': file_name,
281 | 'content': content
282 | },
283 | dataType: 'json',
284 | cache: false,
285 | success: function (data) {
286 | if (data.status && data.status == 'success') {
287 | $('#close-detail').trigger('click');
288 | bsAlert('success', 'Succeed!');
289 | refreshList(type);
290 | }
291 | else {
292 | bsAlert('warning', 'Unknown problem!');
293 | }
294 | },
295 | error: function (err) {
296 | bsAlert('danger', 'Error:' + err.responseJSON.msg);
297 | $content_inner.removeClass('processing');
298 | $publishBtn.removeAttr('disabled');
299 | }
300 | });
301 | },
302 |
303 | // ---- 文件移动相关 ----
304 | // 移到草稿
305 | '#t-unpublish': function () {
306 | moveFileTo('drafts');
307 | },
308 | '#t-delete': function () {
309 | moveFileTo('trash');
310 | },
311 | '#t-publish-post': function () {
312 | moveFileTo('posts');
313 | },
314 | '#t-publish-page': function () {
315 | moveFileTo('pages');
316 | }
317 | }
318 | });
319 |
320 | $('#add-new').click(function () {
321 | showContentDetail({type: "new", tpl: require('../ejs/tpl/new-post.ejs')});
322 | });
323 |
324 | function showContentDetail(data) {
325 | var tpl = data.tpl || require('../ejs/tpl/file-content.ejs');
326 | $('#content-detail-inner').attr('data-type', data.type);
327 | $('#content-detail-body').empty().html(tpl({data: data}));
328 | $('#content-detail-wrap').addClass('show');
329 | $('html, body').addClass('no-scroll');
330 | }
331 |
332 | function moveFileTo(target_type) {
333 | var $tool_wrap = $('#content-tool'),
334 | type = $tool_wrap.attr('data-type'),
335 | index = $tool_wrap.attr('data-index');
336 |
337 | if (!type || !index) {
338 | bsAlert('danger', 'Param error!');
339 | return;
340 | }
341 |
342 | $.ajax({
343 | url: '/move-markdown-file',
344 | method: 'post',
345 | timeout: AJAX_TIMEOUT,
346 | data: {
347 | 'type': type,
348 | 'index': index,
349 | 'target_type': target_type
350 | },
351 | dataType: 'json',
352 | cache: false,
353 | success: function (data) {
354 | if (data.status && data.status == 'success') {
355 | $('#close-detail').trigger('click');
356 | bsAlert('success', 'Success!');
357 | refreshList(target_type);
358 | }
359 | else {
360 | bsAlert('warning', 'moveFileTo unknown problem!');
361 | }
362 | },
363 | error: function (err) {
364 | bsAlert('danger', 'Error:' + err.responseJSON.msg);
365 | }
366 | });
367 | }
368 |
369 | /**
370 | * @param {string} type one of ['posts', 'pages', 'drafts', 'trash']
371 | * */
372 | function refreshList(type) {
373 | type = type || 'posts';
374 | $.ajax({
375 | url: '/get-all',
376 | method: 'get',
377 | timeout: AJAX_TIMEOUT,
378 | dataType: 'json',
379 | cache: false,
380 | success: function (data) {
381 | if (data.status && data.status == 'success') {
382 | data = data.data;
383 |
384 | $('#list-nav').parent().html(require('../ejs/tpl/list.ejs')({data: data, type: type}));
385 |
386 | var tagList = '',
387 | $tagList = $('#tag-list');
388 |
389 | for (var tag in data.tags) {
390 | if (data.tags.hasOwnProperty(tag) && tag !== 'length') {
391 | tagList += '' + tag + ' ( ' + data.tags[tag].length + ' )' + '';
392 | }
393 | }
394 |
395 | $tagList.find('h3').text(data.tags.length + ' tags');
396 | $tagList.find('ul').html(tagList);
397 |
398 | return;
399 | }
400 |
401 | bsAlert('warning', 'refreshList unknown problem!');
402 | },
403 | error: function (err) {
404 | bsAlert('danger', 'Error:' + err.responseJSON);
405 | }
406 | });
407 | }
408 |
409 | var alertTime;
410 | /**
411 | * @param {string} type one of ['success', 'info', 'warning', 'danger']
412 | * @param {string} html
413 | * */
414 | function bsAlert(type, html) {
415 | var $alert = $('#alert');
416 |
417 | if ($alert.hasClass('show')) {
418 | $alert.removeClass('show');
419 | setTimeout(action, 400);
420 | }
421 | else {
422 | action();
423 | }
424 |
425 | function action() {
426 | $alert.removeClass('alert-success alert-info alert-danger alert-warning');
427 | $alert.addClass('alert-' + type + ' show').html(html);
428 | }
429 |
430 | if (alertTime) clearTimeout(alertTime);
431 | alertTime = setTimeout(function () {
432 | $alert.removeClass('show');
433 | }, 3000);
434 | }
435 |
436 | function isFilePathNameValid(str) {
437 | return str.replace(/[a-zA-Z0-9]|_|-/g, '').length === 0;
438 | }
--------------------------------------------------------------------------------
/views/public/js/jquery.min.js:
--------------------------------------------------------------------------------
1 | /*! jQuery v3.1.1-pre | (c) jQuery Foundation | jquery.org/license */
2 | !function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.1.1-pre",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null!=a?a<0?this[a+this.length]:this[a]:f.call(this)},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"label"in b&&b.disabled===a||"form"in b&&b.disabled===a||"form"in b&&b.disabled===!1&&(b.isDisabled===a||b.isDisabled!==!a&&("label"in b||!ea(b))!==a)}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(_,aa),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=V.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(_,aa),$.test(j[0].type)&&qa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&sa(j),!a)return G.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||$.test(a)&&qa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){if(r.isFunction(b))return r.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return r.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(C.test(b))return r.filter(b,a,c);b=r.filter(b,a)}return r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType})}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/\S+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R),a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,
3 | r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,ja=/^$|\/(?:java|ecma)script/i,ka={option:[1,""],thead:[1,""],col:[2,""],tr:[2,""],td:[3,""],_default:[0,"",""]};ka.optgroup=ka.option,ka.tbody=ka.tfoot=ka.colgroup=ka.caption=ka.thead,ka.th=ka.td;function la(a,b){var c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function ma(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=la(l.appendChild(f),"script"),j&&ma(g),c){k=0;while(f=g[k++])ja.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var pa=d.documentElement,qa=/^key/,ra=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,sa=/^([^.]*)(?:\.(.+)|)/;function ta(){return!0}function ua(){return!1}function va(){try{return d.activeElement}catch(a){}}function wa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)wa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=ua;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(pa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=sa.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=sa.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c-1:r.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h\x20\t\r\n\f]*)[^>]*)\/>/gi,ya=/