move to vue-blogify
No files matched your search
@@ -0,0 +1,2 @@
|
||||
> 1%
|
||||
last 2 versions
|
||||
@@ -0,0 +1 @@
|
||||
public/posts/* linguist-vendored
|
||||
@@ -1,125 +1,21 @@
|
||||
|
||||
# Created by https://www.gitignore.io/api/node,macos
|
||||
# Edit at https://www.gitignore.io/?templates=node,macos
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# End of https://www.gitignore.io/api/node,macos
|
||||
|
||||
/dist/
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -1,6 +0,0 @@
|
||||
[submodule "plugins/cogear-plugin-blog"]
|
||||
path = plugins/cogear-plugin-blog
|
||||
url = https://github.com/patarapolw/cogear-plugin-blog.git
|
||||
[submodule "plugins/cogear-plugin-base-url"]
|
||||
path = plugins/cogear-plugin-base-url
|
||||
url = https://github.com/patarapolw/cogear-plugin-base-url.git
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Pacharapol Withayasakpunt
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1 +1,28 @@
|
||||
Clone using `git clone --recursive git@repo.org:your_repo.git`.
|
||||
# Vue Blogify
|
||||
|
||||
Vue CLI-based static blogging platform
|
||||
|
||||
## How to use
|
||||
|
||||
- Edit `config.yaml`
|
||||
- Edit layouts (using `*.vue`) in `src/layouts/`
|
||||
- Add posts in `public/posts`, preferrably in `public/posts/<YEAR>/<MONTH>`. HTML, Pug and Markdown are supported.
|
||||
|
||||
## Installation
|
||||
|
||||
- I use NPM, and Vue CLI 3, so `npm install`
|
||||
- Run using `npm run dev`
|
||||
|
||||
## Building and deploying
|
||||
|
||||
- Build using `npm run build`
|
||||
- Deploy as static website, maybe using [gh-pages](https://www.npmjs.com/package/gh-pages).
|
||||
- To deploy to most static websites, set `publicPath` in `vue.config.js` to `/`.
|
||||
- For non-user GitHub Pages, set `publicPath` to `/<REPO_NAME>`.
|
||||
- For Cordova, Electron, or local, set `publicPath` to `""`.
|
||||
- Note that, if `publicPath` is not `/`, [Vue router's History Mode](https://router.vuejs.org/guide/essentials/history-mode.html) might not work.
|
||||
- For instructions, see <https://cli.vuejs.org/guide/deployment.html>
|
||||
|
||||
## Example
|
||||
|
||||
See <https://vue-blogify.patarapolw.now.sh>
|
||||
@@ -0,0 +1,10 @@
|
||||
module.exports = {
|
||||
"presets": [
|
||||
[
|
||||
"@vue/app",
|
||||
{
|
||||
"useBuiltIns": "entry"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -1,24 +1,19 @@
|
||||
title: 中文 Diary
|
||||
description:
|
||||
keywords:
|
||||
theme: blog
|
||||
baseUrl: /
|
||||
blog:
|
||||
index: "" # Provide posts to index page
|
||||
regex: "^p/"
|
||||
tagUri: 'tag'
|
||||
tabs:
|
||||
- name: 千字文
|
||||
to: /tag/千字文
|
||||
- name: 句子
|
||||
to: /tag/句子
|
||||
- name: 分类词汇
|
||||
to: /tag/分类词汇
|
||||
posts:
|
||||
perPage: 5
|
||||
split: ===
|
||||
title: "中文 Diary"
|
||||
tags:
|
||||
- 千字文
|
||||
- 句子
|
||||
- 分类词汇
|
||||
pages:
|
||||
^/?p/:
|
||||
layout: post
|
||||
author:
|
||||
login: polvcode
|
||||
avatar: 'https://en.gravatar.com/userimage/35152960/7140205ee2a71cbe54d3f2ac43c0370d.jpg?size=24'
|
||||
link: https://polvcode.dev
|
||||
output: dist/
|
||||
about: true
|
||||
external:
|
||||
disqus: zhdiary
|
||||
google:
|
||||
analytics: UA-146246917-1
|
||||
@@ -1,21 +0,0 @@
|
||||
const ghPages = require("gh-pages");
|
||||
const {spawnSync} = require("child_process");
|
||||
const rimraf = require("rimraf");
|
||||
|
||||
rimraf.sync("node_modules/gh-pages/.cache")
|
||||
|
||||
spawnSync("cogear", ["build"], {
|
||||
stdio: "inherit"
|
||||
});
|
||||
|
||||
ghPages.publish("dist", {
|
||||
branch: "master",
|
||||
repo: "https://git.heroku.com/zhdiary.git"
|
||||
}, (err) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Posted to https://zhdiary.herokuapp.com")
|
||||
});
|
||||
@@ -1,41 +1,56 @@
|
||||
{
|
||||
"name": "zhdiary",
|
||||
"version": "1.0.0",
|
||||
"description": "My journey through Chinese language",
|
||||
"main": "server.js",
|
||||
"name": "vue-blogify",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"types": "src/declaration.d.ts",
|
||||
"scripts": {
|
||||
"dev": "vue-cli-service serve:async",
|
||||
"build": "vue-cli-service build:async",
|
||||
"test": "ts-mocha -p tsconfig.test.json src/**/*.spec.ts",
|
||||
"deploy": "npm run build && gh-pages -d dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/jquery": "^3.3.31"
|
||||
"@types/uuid": "^3.4.5",
|
||||
"bootstrap-vue": "^2.0.0-rc.19",
|
||||
"core-js": "^2.6.5",
|
||||
"esm": "^3.2.25",
|
||||
"fast-glob": "^3.0.4",
|
||||
"gray-matter": "^4.0.2",
|
||||
"hyperpug": "^0.1.7",
|
||||
"hyperscript": "^2.0.2",
|
||||
"js-yaml": "^3.13.1",
|
||||
"moment": "^2.24.0",
|
||||
"showdown": "^1.9.0",
|
||||
"uuid": "^3.3.3",
|
||||
"vue": "^2.6.10",
|
||||
"vue-class-component": "^7.0.2",
|
||||
"vue-disqus": "^3.0.5",
|
||||
"vue-property-decorator": "^8.1.0",
|
||||
"vue-router": "^3.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"gh-pages": "^2.1.1",
|
||||
"husky": "^3.0.3",
|
||||
"hyperscript": "^2.0.2",
|
||||
"jstransformer-markdown-it": "^2.1.0",
|
||||
"p-iteration": "^1.1.8",
|
||||
"pug": "^2.0.4",
|
||||
"showdown": "^1.9.0",
|
||||
"webpack": "^4.39.1",
|
||||
"webpack-merge": "^4.2.1"
|
||||
"@babel/polyfill": "^7.4.4",
|
||||
"@types/expect": "^1.20.4",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@vue/cli-plugin-babel": "^3.10.0",
|
||||
"@vue/cli-plugin-typescript": "^3.10.0",
|
||||
"@vue/cli-service": "^3.10.0",
|
||||
"bootstrap": "^4.3.1",
|
||||
"mocha": "^6.2.0",
|
||||
"mutationobserver-shim": "^0.3.3",
|
||||
"node-sass": "^4.12.0",
|
||||
"popper.js": "^1.15.0",
|
||||
"portal-vue": "^2.1.4",
|
||||
"sass-loader": "^7.1.0",
|
||||
"ts-mocha": "^6.0.0",
|
||||
"typescript": "^3.4.3",
|
||||
"vue-cli-plugin-bootstrap-vue": "^0.4.0",
|
||||
"vue-cli-plugin-pug": "^1.0.7",
|
||||
"vue-template-compiler": "^2.6.10"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "cogear",
|
||||
"build": "cogear build",
|
||||
"deploy": "node deploy.js"
|
||||
},
|
||||
"author": {
|
||||
"name": "Pacharapol Withayasakpunt",
|
||||
"email": "patarapolw@gmail.com",
|
||||
"url": "https://polvcode.dev"
|
||||
},
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/patarapolw/zhdiary.git"
|
||||
},
|
||||
"homepage": "https://zhdiary.herokuapp.com",
|
||||
"husky": {},
|
||||
"engines": {
|
||||
"node": "10.16.0",
|
||||
"npm": "6.10.0"
|
||||
"vuePlugins": {
|
||||
"service": [
|
||||
"vue.async.config.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": "custom-render",
|
||||
"description": "Cogear.JS custom Webpack plugin.",
|
||||
"version": "1.0.0",
|
||||
"author": "Pacharapol Withayasakpunt<patarapolw@gmail.com>",
|
||||
"homepage": "https://github.com/patarapolw/custom-webpack",
|
||||
"main": "./plugin.js"
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
const { Markdown } = require("./plugin");
|
||||
|
||||
const md = new Markdown();
|
||||
console.log(md.md2html(`
|
||||
a | b | c
|
||||
d | e | f
|
||||
`));
|
||||
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
autoprefixer: {}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 440 B After Width: | Height: | Size: 440 B |
|
Before Width: | Height: | Size: 912 B After Width: | Height: | Size: 912 B |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="<%= BASE_URL %>apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="<%= BASE_URL %>favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="<%= BASE_URL %>favicon-16x16.png">
|
||||
<link rel="manifest" href="<%= BASE_URL %>site.webmanifest">
|
||||
<link rel="stylesheet" href="<%= BASE_URL %>index.css">
|
||||
<title>中文 Diary - Record my journey to Chinese language</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but vue-blogify doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: "千字文"
|
||||
date: "2019-08-10"
|
||||
tags:
|
||||
tag:
|
||||
- 千字文
|
||||
- 汉字
|
||||
---
|
||||
@@ -1,12 +1,13 @@
|
||||
---
|
||||
title: "千字文 - 1/1"
|
||||
date: "2019-08-11"
|
||||
tags:
|
||||
tag:
|
||||
- 千字文
|
||||
- 汉字
|
||||
---
|
||||
|
||||
.w-100.mt-3: h3.text-center 天地玄黃,宇宙洪荒。
|
||||
.w-100.mt-3
|
||||
h3.text-center 天地玄黃,宇宙洪荒。
|
||||
.w-100
|
||||
figure.d-inline-block
|
||||
img(src="https://upload.wikimedia.org/wikipedia/commons/a/ab/天-order.gif" height=100)
|
||||
@@ -34,7 +35,7 @@ tags:
|
||||
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/33618.gif" height=100)
|
||||
figcaption.text-center huang1
|
||||
|
||||
| ===
|
||||
===
|
||||
|
||||
.w-100.mt-3
|
||||
:markdown
|
||||
@@ -1,12 +1,13 @@
|
||||
---
|
||||
title: "千字文 - 1/2"
|
||||
date: "2019-08-12"
|
||||
tags:
|
||||
tag:
|
||||
- 千字文
|
||||
- 汉字
|
||||
---
|
||||
|
||||
.w-100.mt-3: h3.text-center 日月盈昃,辰宿列張。
|
||||
.w-100.mt-3
|
||||
h3.text-center 日月盈昃,辰宿列張。
|
||||
.w-100
|
||||
figure.d-inline-block
|
||||
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/26085.gif" height=100)
|
||||
@@ -34,7 +35,7 @@ tags:
|
||||
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/24373.gif" height=100)
|
||||
figcaption.text-center zhang1
|
||||
|
||||
| ===
|
||||
===
|
||||
|
||||
.w-100.mt-3
|
||||
:markdown
|
||||
@@ -1,12 +1,13 @@
|
||||
---
|
||||
title: "千字文 - 1/3"
|
||||
date: "2019-08-13"
|
||||
tags:
|
||||
tag:
|
||||
- 千字文
|
||||
- 汉字
|
||||
---
|
||||
|
||||
.w-100.mt-3: h3.text-center 寒來暑往,秋收冬藏。
|
||||
.w-100.mt-3
|
||||
h3.text-center 寒來暑往,秋收冬藏。
|
||||
.w-100
|
||||
figure.d-inline-block
|
||||
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/23506.gif" height=100)
|
||||
@@ -34,7 +35,7 @@ tags:
|
||||
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/34255.gif" height=100)
|
||||
figcaption.text-center cang2
|
||||
|
||||
| ===
|
||||
===
|
||||
|
||||
.w-100.mt-3
|
||||
:markdown
|
||||
@@ -1,12 +1,13 @@
|
||||
---
|
||||
title: "千字文 - 1/4"
|
||||
date: "2019-08-14"
|
||||
tags:
|
||||
tag:
|
||||
- 千字文
|
||||
- 汉字
|
||||
---
|
||||
|
||||
.w-100.mt-3: h3.text-center 閏餘成歲,律呂調陽。
|
||||
.w-100.mt-3
|
||||
h3.text-center 閏餘成歲,律呂調陽。
|
||||
.w-100
|
||||
figure.d-inline-block
|
||||
.hanzi-img 閏
|
||||
@@ -34,7 +35,7 @@ tags:
|
||||
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/38525.gif" height=100)
|
||||
figcaption.text-center yang2
|
||||
|
||||
| ===
|
||||
===
|
||||
|
||||
.w-100.mt-3
|
||||
:markdown
|
||||
@@ -1,12 +1,13 @@
|
||||
---
|
||||
title: "千字文 - 1/5"
|
||||
date: "2019-08-15"
|
||||
tags:
|
||||
tag:
|
||||
- 千字文
|
||||
- 汉字
|
||||
---
|
||||
|
||||
.w-100.mt-3: h3.text-center 雲騰致雨,露結為霜。
|
||||
.w-100.mt-3
|
||||
h3.text-center 雲騰致雨,露結為霜。
|
||||
.w-100
|
||||
figure.d-inline-block
|
||||
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/38642.gif" height=100)
|
||||
@@ -34,7 +35,7 @@ tags:
|
||||
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/38684.gif" height=100)
|
||||
figcaption.text-center shuang1
|
||||
|
||||
| ===
|
||||
===
|
||||
|
||||
.w-100.mt-3
|
||||
:markdown
|
||||
@@ -1,12 +1,13 @@
|
||||
---
|
||||
title: "千字文 - 1/6"
|
||||
date: "2019-08-16"
|
||||
tags:
|
||||
tag:
|
||||
- 千字文
|
||||
- 汉字
|
||||
---
|
||||
|
||||
.w-100.mt-3: h3.text-center 金生麗水,玉出崑岡。
|
||||
.w-100.mt-3
|
||||
h3.text-center 金生麗水,玉出崑岡。
|
||||
.w-100
|
||||
figure.d-inline-block
|
||||
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/37329.gif" height=100)
|
||||
@@ -34,7 +35,7 @@ tags:
|
||||
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/23713.gif" height=100)
|
||||
figcaption.text-center gang1
|
||||
|
||||
| ===
|
||||
===
|
||||
|
||||
.w-100.mt-3
|
||||
:markdown
|
||||
@@ -1,12 +1,13 @@
|
||||
---
|
||||
title: "千字文 - 1/7"
|
||||
date: "2019-08-17"
|
||||
tags:
|
||||
tag:
|
||||
- 千字文
|
||||
- 汉字
|
||||
---
|
||||
|
||||
.w-100.mt-3: h3.text-center 劍號巨闕,珠稱夜光。
|
||||
.w-100.mt-3
|
||||
h3.text-center 劍號巨闕,珠稱夜光。
|
||||
.w-100
|
||||
figure.d-inline-block
|
||||
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/21133.gif" height=100)
|
||||
@@ -34,7 +35,7 @@ tags:
|
||||
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/20809.gif" height=100)
|
||||
figcaption.text-center guang1
|
||||
|
||||
| ===
|
||||
===
|
||||
|
||||
.w-100.mt-3
|
||||
:markdown
|
||||
@@ -1,12 +1,13 @@
|
||||
---
|
||||
title: "千字文 - 1/8"
|
||||
date: "2019-08-18"
|
||||
tags:
|
||||
tag:
|
||||
- 千字文
|
||||
- 汉字
|
||||
---
|
||||
|
||||
.w-100.mt-3: h3.text-center 果珍李柰,菜重芥薑。
|
||||
.w-100.mt-3
|
||||
h3.text-center 果珍李柰,菜重芥薑。
|
||||
.w-100
|
||||
figure.d-inline-block
|
||||
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/26524.gif" height=100)
|
||||
@@ -34,7 +35,7 @@ tags:
|
||||
.hanzi-img 薑
|
||||
figcaption.text-center jiang1
|
||||
|
||||
| ===
|
||||
===
|
||||
|
||||
.w-100.mt-3
|
||||
:markdown
|
||||
@@ -1,12 +1,13 @@
|
||||
---
|
||||
title: "千字文 - 1/9"
|
||||
date: "2019-08-19"
|
||||
tags:
|
||||
tag:
|
||||
- 千字文
|
||||
- 汉字
|
||||
---
|
||||
|
||||
.w-100.mt-3: h3.text-center 海咸河淡,鱗潛羽翔。
|
||||
.w-100.mt-3
|
||||
h3.text-center 海咸河淡,鱗潛羽翔。
|
||||
.w-100
|
||||
figure.d-inline-block
|
||||
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/28023.gif" height=100)
|
||||
@@ -34,7 +35,7 @@ tags:
|
||||
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/32724.gif" height=100)
|
||||
figcaption.text-center xiang2
|
||||
|
||||
| ===
|
||||
===
|
||||
|
||||
.w-100.mt-3
|
||||
:markdown
|
||||
@@ -1,12 +1,13 @@
|
||||
---
|
||||
title: "千字文 - 2/1"
|
||||
date: "2019-08-20"
|
||||
tags:
|
||||
tag:
|
||||
- 千字文
|
||||
- 汉字
|
||||
---
|
||||
|
||||
.w-100.mt-3: h3.text-center 龍師火帝,鳥官人皇。
|
||||
.w-100.mt-3
|
||||
h3.text-center 龍師火帝,鳥官人皇。
|
||||
.w-100
|
||||
figure.d-inline-block
|
||||
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/40845.gif" height=100)
|
||||
@@ -34,7 +35,7 @@ tags:
|
||||
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/30343.gif" height=100)
|
||||
figcaption.text-center huang1
|
||||
|
||||
| ===
|
||||
===
|
||||
|
||||
.w-100.mt-3
|
||||
:markdown
|
||||
@@ -1,12 +1,13 @@
|
||||
---
|
||||
title: "千字文 - 2/2"
|
||||
date: "2019-08-21"
|
||||
tags:
|
||||
tag:
|
||||
- 千字文
|
||||
- 汉字
|
||||
---
|
||||
|
||||
.w-100.mt-3: h3.text-center 始制文字,乃服衣裳。
|
||||
.w-100.mt-3
|
||||
h3.text-center 始制文字,乃服衣裳。
|
||||
.w-100
|
||||
figure.d-inline-block
|
||||
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/22987.gif" height=100)
|
||||
@@ -34,7 +35,7 @@ tags:
|
||||
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/35059.gif" height=100)
|
||||
figcaption.text-center chang2
|
||||
|
||||
| ===
|
||||
===
|
||||
|
||||
.w-100.mt-3
|
||||
:markdown
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: "句子 1.1"
|
||||
date: "2019-08-16 1:00"
|
||||
tags:
|
||||
date: "2019-08-16 01:00"
|
||||
tag:
|
||||
- 千字文
|
||||
- 句子
|
||||
---
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: "句子 1.2"
|
||||
date: "2019-08-17 1:00"
|
||||
tags:
|
||||
date: "2019-08-17 01:00"
|
||||
tag:
|
||||
- 千字文
|
||||
- 句子
|
||||
---
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: "句子 1.3"
|
||||
date: "2019-08-17 2:00"
|
||||
tags:
|
||||
date: "2019-08-17 02:00"
|
||||
tag:
|
||||
- 千字文
|
||||
- 句子
|
||||
---
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: "身体1"
|
||||
date: "2019-08-18 1:00"
|
||||
tags:
|
||||
date: "2019-08-18 01:00"
|
||||
tag:
|
||||
- 分类词汇
|
||||
- 身体
|
||||
---
|
||||
@@ -15,7 +15,7 @@ tags:
|
||||
|
||||
Highlight text and press "x" inside the presentation to speak.
|
||||
|
||||
| ===
|
||||
===
|
||||
|
||||
:markdown
|
||||
胳膊 | gēbo | arm
|
||||
@@ -0,0 +1,34 @@
|
||||
<template lang="pug">
|
||||
div
|
||||
b-navbar(toggleable="lg" type="dark" variant="success")
|
||||
b-navbar-brand(to="/") {{title}}
|
||||
b-navbar-toggle(target="nav-collapse")
|
||||
b-collapse#nav-collapse(is-nav)
|
||||
b-navbar-nav
|
||||
b-nav-item(v-for="t in tabs" :key="t.name" :to="t.to" :url="t.url") {{t.name}}
|
||||
b-navbar-nav.ml-auto
|
||||
b-nav-form(@submit.stop.prevent)
|
||||
b-form-input.mr-sm-2(size="sm" placeholder="Type to search" style="min-width: 200px" v-model="g.q")
|
||||
b-nav-item(to="/about") About
|
||||
b-container.mt-3
|
||||
b-row
|
||||
b-col(sm=12 lg=8)
|
||||
router-view
|
||||
b-col(sm=12 lg=4)
|
||||
sidebar
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Vue, Component } from "vue-property-decorator";
|
||||
import Sidebar from "@/layouts/Sidebar.vue";
|
||||
import { g } from "@/shared";
|
||||
|
||||
@Component({
|
||||
components: {Sidebar}
|
||||
})
|
||||
export default class App extends Vue {
|
||||
private title = g.config.title || "BLOG"
|
||||
private tabs = g.config.tabs || []
|
||||
private g = g;
|
||||
}
|
||||
</script>
|
||||
@@ -1,39 +0,0 @@
|
||||
import "./disqus";
|
||||
import "./app.scss";
|
||||
|
||||
window.addEventListener("keydown", (ev) => {
|
||||
if (ev.key === "s") {
|
||||
const s = window.getSelection().toString();
|
||||
if (s) {
|
||||
speak(s);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
window.onload = () => {
|
||||
for (const a of Array.from(document.getElementsByTagName("a"))) {
|
||||
const href = a.href;
|
||||
console.log(href);
|
||||
const m = /^slide:github:([^/]+)\/([^/]+)\/(.+)$/.exec(href);
|
||||
if (m) {
|
||||
const url = new URL("https://reveal-md.herokuapp.com/");
|
||||
url.searchParams.append("q", `https://raw.githubusercontent.com/${m[1]}/${m[2]}/master/src/resources/${m[3]}`);
|
||||
a.href = url.href;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function speak(s: string) {
|
||||
const allVoices = speechSynthesis.getVoices();
|
||||
let vs = allVoices.filter((v) => /zh[-_]cn/i.test(v.lang));
|
||||
if (vs.length === 0) {
|
||||
vs = allVoices.filter((v) => /zh/i.test(v.lang));
|
||||
}
|
||||
|
||||
if (vs.length > 0) {
|
||||
const u = new SpeechSynthesisUtterance(s);
|
||||
u.lang = vs[0].lang;
|
||||
u.rate = 0.8;
|
||||
speechSynthesis.speak(u);
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 6.7 KiB |
@@ -0,0 +1,6 @@
|
||||
declare const POSTS: Record<string, any>[];
|
||||
declare const CONFIG: Record<string, any>;
|
||||
declare const README: string;
|
||||
|
||||
declare module "@/plugins/render";
|
||||
declare module 'vue-disqus';
|
||||
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
|
||||
* LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/
|
||||
/*
|
||||
var disqus_config = function () {
|
||||
this.page.url = PAGE_URL; // Replace PAGE_URL with your page's canonical URL variable
|
||||
this.page.identifier = PAGE_IDENTIFIER; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
|
||||
};
|
||||
*/
|
||||
(function() { // DON'T EDIT BELOW THIS LINE
|
||||
var d = document, s = d.createElement('script');
|
||||
s.src = 'https://zhdiary.disqus.com/embed.js';
|
||||
s.setAttribute('data-timestamp', +new Date());
|
||||
(d.head || d.body).appendChild(s);
|
||||
})();
|
||||
@@ -0,0 +1,11 @@
|
||||
<template lang="pug">
|
||||
:markdown
|
||||
### Sorry, but no post can be found.
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue'
|
||||
export default Vue.extend({
|
||||
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,20 @@
|
||||
<template lang="pug">
|
||||
b-card
|
||||
header: h1 Write a post!
|
||||
.post-content
|
||||
p Go to <code>./public/posts</code> folder and create your first post, creating <code>first-post.md</code> file filled with the following content:
|
||||
pre.
|
||||
---
|
||||
title: First post
|
||||
tag:
|
||||
- news
|
||||
---
|
||||
# This is my first post!
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue'
|
||||
export default Vue.extend({
|
||||
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,116 @@
|
||||
<template lang="pug">
|
||||
b-card.mb-3.post
|
||||
.post-meta.mb-3
|
||||
.float-right {{dateString}}
|
||||
.post-meta-author
|
||||
a(:href="author.link"): b-img(rounded="circle" :src="author.avatar")
|
||||
a(:href="author.link") {{author.login}}
|
||||
h2
|
||||
b-link(v-if="isTeaser" :to="to") {{title}}
|
||||
span(v-else="") {{title}}
|
||||
div
|
||||
div(v-if="isTeaser" v-html="teaser")
|
||||
div(v-else="" v-html="content")
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Vue, Component, Prop } from "vue-property-decorator";
|
||||
import moment, { Moment } from "moment";
|
||||
import matter from "gray-matter";
|
||||
import HyperPug from "hyperpug";
|
||||
import { pugFilters, md } from "@/plugins/render";
|
||||
import { g } from '../shared';
|
||||
|
||||
@Component
|
||||
export default class Post extends Vue {
|
||||
@Prop() private filename!: string;
|
||||
@Prop({default: false}) private isTeaser!: boolean;
|
||||
|
||||
private matter: any = {};
|
||||
|
||||
get title() {
|
||||
if (this.matter.data) {
|
||||
return this.matter.data.title || ""
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
get moment(): Moment | null {
|
||||
if (this.matter.data && this.matter.data.date) {
|
||||
return moment(this.matter.data.date);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
get dateString() {
|
||||
return this.moment ? this.moment.format("ddd D MMMM YYYY") : "";
|
||||
}
|
||||
|
||||
get author() {
|
||||
const author = (this.matter.data ? this.matter.data.author : null) || g.config.author;
|
||||
if (!author) {
|
||||
return {
|
||||
link: "#",
|
||||
avatar: "#",
|
||||
login: "Anonymous"
|
||||
}
|
||||
}
|
||||
|
||||
return author;
|
||||
}
|
||||
|
||||
get url() {
|
||||
return this.filename.replace(/^public\//, process.env.BASE_URL || "/")
|
||||
}
|
||||
|
||||
get to() {
|
||||
const m = this.moment;
|
||||
if (m) {
|
||||
return `/p/${m.format("YYYY")}/${m.format("MM")}/${
|
||||
this.filename
|
||||
.replace(/^.*\//, "")
|
||||
.replace(/\.[^.]+$/, "")}`;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
get teaser() {
|
||||
const { content } = this.matter;
|
||||
const m = /^([^]+)\s+===\s+([^]+)$/.exec(content || "");
|
||||
if (m) {
|
||||
return this.parseContent(m[1]);
|
||||
}
|
||||
|
||||
return this.parseContent(content || "");
|
||||
}
|
||||
|
||||
get content() {
|
||||
const { content } = this.matter;
|
||||
const m = /^([^]+)\s+===\s+([^]+)$/.exec(content || "");
|
||||
console.log(m)
|
||||
if (m) {
|
||||
return this.parseContent(m[1] + (m[2] || ""));
|
||||
}
|
||||
|
||||
return this.parseContent(content || "");
|
||||
}
|
||||
|
||||
public async mounted() {
|
||||
console.log(this.url);
|
||||
this.matter = matter(await (await fetch(this.url)).text());
|
||||
}
|
||||
|
||||
private parseContent(s: string): string {
|
||||
if (/\.pug$/.test(this.filename)) {
|
||||
s = new HyperPug(pugFilters).parse(s);
|
||||
} else if (/\.html?$/.test(this.filename)) {
|
||||
return s;
|
||||
}
|
||||
|
||||
return md.md2html(s);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,84 @@
|
||||
<template lang="pug">
|
||||
div
|
||||
b-card.mb-3
|
||||
:markdown
|
||||
#### Record my journey to Chinese by [@polvcode](https://polvcode.dev)
|
||||
|
||||
Highlight text and press "s" to speak.
|
||||
a.twitter-timeline(href="https://twitter.com/ChinesePol" data-height="600") Tweets by ChinesePol
|
||||
b-card
|
||||
h3 Tag cloud
|
||||
span.mr-3(v-for="t in tags" :key="t.name" :class="t.class")
|
||||
b-link(:to="'/tag/' + t.name") {{t.name}}
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Vue, Component } from "vue-property-decorator";
|
||||
import { g } from '../shared';
|
||||
|
||||
@Component
|
||||
export default class Sidebar extends Vue {
|
||||
get tags() {
|
||||
const tagList: Record<string, number> = {};
|
||||
|
||||
g.posts.forEach((p) => {
|
||||
for (const t of p.tag || []) {
|
||||
tagList[t] = (tagList[t] || 0) + 1;
|
||||
}
|
||||
});
|
||||
|
||||
return Object.keys(tagList).sort().map((t) => {
|
||||
return {
|
||||
name: t,
|
||||
class: (() => {
|
||||
const count = tagList[t];
|
||||
if (count > 30) {
|
||||
return "c30"
|
||||
} else if (count > 20) {
|
||||
return "c20"
|
||||
} else if (count > 10) {
|
||||
return "c10"
|
||||
} else if (count > 5) {
|
||||
return "c5"
|
||||
} else if (count > 3) {
|
||||
return "c3"
|
||||
} else if (count > 1) {
|
||||
return "c2"
|
||||
}
|
||||
return "c1"
|
||||
})()
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.c30 {
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.c20 {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.c10 {
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
.c5 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.c3 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.c2 {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.c1 {
|
||||
font-size: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,16 @@
|
||||
import '@babel/polyfill';
|
||||
import 'mutationobserver-shim';
|
||||
import Vue from 'vue';
|
||||
import './plugins/bootstrap-vue';
|
||||
import App from './App.vue';
|
||||
import router from './router';
|
||||
import VueDisqus from 'vue-disqus';
|
||||
import "./plugins/gtag";
|
||||
|
||||
Vue.config.productionTip = false
|
||||
Vue.use(VueDisqus)
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
@@ -1,3 +0,0 @@
|
||||
---
|
||||
title: '中文 Diary - My Chinese learning diary'
|
||||
---
|
||||
@@ -0,0 +1,7 @@
|
||||
import Vue from 'vue'
|
||||
|
||||
import BootstrapVue from 'bootstrap-vue'
|
||||
import 'bootstrap/dist/css/bootstrap.min.css'
|
||||
import 'bootstrap-vue/dist/bootstrap-vue.css'
|
||||
|
||||
Vue.use(BootstrapVue)
|
||||
@@ -0,0 +1,22 @@
|
||||
import { g } from "@/shared";
|
||||
import { dotGetter } from './search';
|
||||
|
||||
declare const dataLayer: any[];
|
||||
|
||||
async function gAnalytics(id: string) {
|
||||
var d = document, s = d.createElement('script');
|
||||
s.src = `https://www.googletagmanager.com/gtag/js?id=${id}`;
|
||||
|
||||
s.setAttribute('data-timestamp', new Date().toISOString());
|
||||
(d.head || d.body).appendChild(s);
|
||||
|
||||
(window as any).dataLayer = dataLayer || [];
|
||||
function gtag(...x: any[]){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', id);
|
||||
}
|
||||
|
||||
const id = dotGetter(g.config, "external.google.analytics");
|
||||
if (id) {
|
||||
gAnalytics(id);
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
const pug = require("pug");
|
||||
const showdown = require("showdown");
|
||||
const h = require("hyperscript");
|
||||
import showdown from "showdown";
|
||||
import h from "hyperscript";
|
||||
|
||||
class Markdown {
|
||||
export class Markdown {
|
||||
constructor() {
|
||||
this.converter = new showdown.Converter({
|
||||
parseImgDimensions: true
|
||||
@@ -33,25 +32,12 @@ class Markdown {
|
||||
}
|
||||
}
|
||||
|
||||
const md = new Markdown();
|
||||
|
||||
module.exports = {
|
||||
Markdown,
|
||||
apply() {
|
||||
cogear.on("preload.page.parse", (page) => {
|
||||
if (page.format === ".pug") {
|
||||
page.__content = pug.compile(page.__content || "", {
|
||||
filters: {
|
||||
export const md = new Markdown();
|
||||
export const pugFilters = {
|
||||
markdown: (text) => {
|
||||
return md.md2html(text)
|
||||
}
|
||||
}
|
||||
})()
|
||||
page.format = ".html";
|
||||
} else if (page.format === ".md") {
|
||||
page.__content = md.md2html(page.__content || "")
|
||||
page.format = ".html";
|
||||
}
|
||||
})
|
||||
}
|
||||
return md.md2html(text);
|
||||
},
|
||||
css: (text) => {
|
||||
return h("style", {innerHTML: text});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
import SearchParser from "./search";
|
||||
|
||||
const p = new SearchParser();
|
||||
|
||||
describe("SearchParser.doParse", () => {
|
||||
[
|
||||
"",
|
||||
"a",
|
||||
"a:b",
|
||||
"a:b c",
|
||||
"a:b c:d",
|
||||
"a:b OR c",
|
||||
"date:2018-08-12",
|
||||
"date>2018-08-12",
|
||||
" tag:分类词汇"
|
||||
].forEach((q) => {
|
||||
it(q, () => {
|
||||
console.dir(p.parseFull(q), {depth: null});
|
||||
})
|
||||
});
|
||||
})
|
||||
@@ -0,0 +1,396 @@
|
||||
/**
|
||||
* Adapted from https://github.com/patarapolw/rep2recall-web/blob/master/src/node/engine/search.ts
|
||||
*/
|
||||
import uuid from "uuid/v4";
|
||||
import moment from "moment";
|
||||
import { g } from '@/shared';
|
||||
|
||||
export interface ISearchParserResult {
|
||||
is: Set<string>;
|
||||
sortBy: string | null;
|
||||
desc: boolean | null;
|
||||
cond: Record<string, any>;
|
||||
}
|
||||
|
||||
export default class SearchParser {
|
||||
public is: Set<string> = new Set();
|
||||
public sortBy: string | null = null;
|
||||
public desc: boolean | null = null;
|
||||
|
||||
private readonly anyOf: Set<string> | null = null;
|
||||
private readonly isString = new Set(["title", "author", "tag"]);
|
||||
private readonly isDate = new Set(["date"]);
|
||||
|
||||
public filterPosts(q: string): Record<string, any>[] {
|
||||
const cond = this.parse(q);
|
||||
|
||||
let ps = g.posts;
|
||||
if (this.sortBy) {
|
||||
ps = ps.sort(sorter(this.sortBy, this.desc || false));
|
||||
} else {
|
||||
ps = ps.sort(sorter("date", true));
|
||||
}
|
||||
|
||||
ps = ps.filter(mongoFilter(cond));
|
||||
|
||||
return ps;
|
||||
}
|
||||
|
||||
public parse(q: string): Record<string, any> {
|
||||
this.is = new Set();
|
||||
this.sortBy = null
|
||||
this.desc = null;
|
||||
|
||||
try {
|
||||
return this._parse(q);
|
||||
} catch (e) { }
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
public parseFull(q: string): ISearchParserResult {
|
||||
return {
|
||||
cond: this.parse(q),
|
||||
is: this.is,
|
||||
sortBy: this.sortBy,
|
||||
desc: this.desc
|
||||
}
|
||||
}
|
||||
|
||||
private _parse(q: string): Record<string, any> {
|
||||
q = q.trim();
|
||||
|
||||
for (const method of [
|
||||
this.removeBrackets,
|
||||
this.parseSep(" OR "),
|
||||
this.parseSep(" "),
|
||||
this.parseNeg,
|
||||
this.parseFullExpr,
|
||||
this.parsePartialExpr
|
||||
]) {
|
||||
try {
|
||||
return method.bind(this)(q);
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
private removeBrackets(q: string) {
|
||||
if (q[0] === "(" && q[q.length - 1] === ")") {
|
||||
return this.parse(q.substr(1, q.length - 2));
|
||||
}
|
||||
|
||||
throw new Error("Not bracketed");
|
||||
}
|
||||
|
||||
private parseSep(sep: string) {
|
||||
return (q: string) => {
|
||||
const brackets: any = {};
|
||||
|
||||
q = q.replace(/\([^)]+\)/g, (p0) => {
|
||||
const id = uuid();
|
||||
brackets[id] = p0;
|
||||
return id;
|
||||
});
|
||||
const tokens = q.split(sep);
|
||||
tokens.forEach((t, i) => {
|
||||
for (const k of Object.keys(brackets)) {
|
||||
tokens[i] = tokens[i].replace(k, brackets[k]);
|
||||
}
|
||||
});
|
||||
|
||||
if (tokens.length >= 2) {
|
||||
const parsedTokens = tokens.map((t) => this._parse(t)).map((t) => t);
|
||||
if (parsedTokens.length > 1) {
|
||||
return { [sep === " OR " ? "$or" : "$and"]: parsedTokens };
|
||||
} else {
|
||||
return parsedTokens[0];
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Not separated by '${sep}'`);
|
||||
}
|
||||
}
|
||||
|
||||
private parseNeg(q: string) {
|
||||
if (q[0] === "-") {
|
||||
const sb = "-sortBy:";
|
||||
if (q.startsWith(sb) && q !== sb) {
|
||||
this.sortBy = q.substr(sb.length);
|
||||
return {};
|
||||
}
|
||||
|
||||
return { $not: this.parse(q.substr(1)) };
|
||||
}
|
||||
|
||||
throw new Error("Not negative");
|
||||
}
|
||||
|
||||
private parseFullExpr(q: string) {
|
||||
const m = /^([\w-]+)(:|~|[><]=?|=)([\S-]+|"[^"]+")$/.exec(q);
|
||||
if (m) {
|
||||
let [_, k, op, v]: any[] = m;
|
||||
|
||||
if (v.length > 2 && v[0] === '"' && v[v.length - 1] === '"') {
|
||||
v = v.substr(1, v.length - 2);
|
||||
} else {
|
||||
const m1 = /^\d+(?:\.\d+)?$/.exec(v);
|
||||
if (m1) {
|
||||
v = parseFloat(v);
|
||||
}
|
||||
}
|
||||
|
||||
if (k === "is") {
|
||||
if (v === "due") {
|
||||
k = "nextReview";
|
||||
op = "<=";
|
||||
v = new Date();
|
||||
} else if (v === "leech") {
|
||||
k = "srsLevel";
|
||||
op = "=";
|
||||
v = 0;
|
||||
} else if (v === "new") {
|
||||
k = "nextReview";
|
||||
op = "=";;
|
||||
v = "NULL";
|
||||
} else {
|
||||
this.is.add(v);
|
||||
return {};
|
||||
}
|
||||
} else if (k === "sortBy") {
|
||||
this.sortBy = v;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (op === ":") {
|
||||
if (k === "due" || k === "nextReview") {
|
||||
k = "nextReview";
|
||||
v = "<="
|
||||
} else if (k === "created" || k === "modified") {
|
||||
v = "<=";
|
||||
}
|
||||
}
|
||||
|
||||
if (v === "NULL") {
|
||||
v = null;
|
||||
}
|
||||
|
||||
if (this.isDate.has(k)) {
|
||||
if (v === "NOW") {
|
||||
v = new Date();
|
||||
} else if (typeof v === "string") {
|
||||
const m1 = /^([-+]?\\d+)(\\S*)$/.exec(v);
|
||||
let isMomentParsed = false;
|
||||
if (m1) {
|
||||
try {
|
||||
v = moment().add(parseInt(m1[1]), m1[2] as any).toDate();
|
||||
isMomentParsed = true;
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
if (!isMomentParsed) {
|
||||
try {
|
||||
v = moment(v).toDate()
|
||||
} catch (e) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if (moment.isDate(v) && op === ":") {
|
||||
// op = ">";
|
||||
// }
|
||||
|
||||
if (v) {
|
||||
if (op === ":") {
|
||||
if (typeof v === "string" || this.isString.has(k)) {
|
||||
v = { $substr: v };
|
||||
}
|
||||
} else if (op === "~") {
|
||||
v = { $regex: v.toString() };
|
||||
} else if (op === ">=") {
|
||||
v = { $gte: v };
|
||||
} else if (op === ">") {
|
||||
v = { $gt: v }
|
||||
} else if (op === "<=") {
|
||||
v = { $lte: v };
|
||||
} else if (op === "<") {
|
||||
v = { $lt: v };
|
||||
}
|
||||
}
|
||||
|
||||
return { [k]: v };
|
||||
}
|
||||
|
||||
throw new Error("Not full expression");
|
||||
}
|
||||
|
||||
|
||||
private parsePartialExpr(q: string) {
|
||||
if (q && q.indexOf(":") === -1) {
|
||||
const orCond: any[] = [];
|
||||
|
||||
if (this.anyOf) {
|
||||
for (const a of this.anyOf) {
|
||||
if (this.isString.has(a)) {
|
||||
orCond.push({ [a]: { $substr: q } });
|
||||
} else {
|
||||
orCond.push({ [a]: q });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return {"*": {$substr: q}}
|
||||
}
|
||||
|
||||
if (orCond.length > 1) {
|
||||
return { $or: orCond };
|
||||
} else if (orCond.length === 1) {
|
||||
return orCond[0];
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
throw new Error("Not partial expression");
|
||||
}
|
||||
}
|
||||
|
||||
export function mongoFilter(cond: any) {
|
||||
return (item: Record<string, any>): boolean => {
|
||||
for (const k of Object.keys(cond)) {
|
||||
if (k[0] === "$") {
|
||||
if (k === "$and") {
|
||||
return cond[k].every((x: Record<string, any>) => mongoFilter(x)(item));
|
||||
} else if (k === "$or") {
|
||||
return cond[k].some((x: Record<string, any>) => mongoFilter(x)(item));
|
||||
} else if (k === "$not") {
|
||||
return !mongoFilter(cond[k])(item);
|
||||
}
|
||||
} else {
|
||||
let itemK = dotGetter(item, k);
|
||||
|
||||
if (cond[k] && cond[k].constructor === {}.constructor
|
||||
&& Object.keys(cond[k]).some((k0) => k0[0] === "$")) {
|
||||
return (() => {
|
||||
for (const op of Object.keys(cond[k])) {
|
||||
try {
|
||||
if (op === "$regex") {
|
||||
if (Array.isArray(itemK)) {
|
||||
return itemK.some(new RegExp(cond[k][op].toString(), "i").test);
|
||||
} else {
|
||||
return new RegExp(cond[k][op].toString(), "i").test(itemK);
|
||||
}
|
||||
} else if (op === "$startswith") {
|
||||
if (Array.isArray(itemK)) {
|
||||
return itemK.some((el) => el.startsWith(cond[k][op]));
|
||||
} else {
|
||||
return itemK.startsWith(cond[k][op]);
|
||||
}
|
||||
} else if (op === "$substr") {
|
||||
|
||||
if (Array.isArray(itemK)) {
|
||||
return itemK.some((el: string) => el.includes(cond[k][op]));
|
||||
} else {
|
||||
return itemK.includes(cond[k][op]);
|
||||
}
|
||||
} else if (op === "$exists") {
|
||||
return (itemK === null || itemK === undefined || itemK === "") === cond[k][op];
|
||||
} else {
|
||||
let v = itemK;
|
||||
let v0 = cond[k][op];
|
||||
try {
|
||||
[v, v0] = [parseInt(v), parseInt(v0)];
|
||||
} catch (e) { }
|
||||
|
||||
if (op === "$gte") {
|
||||
return v >= v0;
|
||||
} else if (op === "$gt") {
|
||||
return v > v0;
|
||||
} else if (op === "$lte") {
|
||||
return v <= v0;
|
||||
} else if (op === "$lt") {
|
||||
return v < v0;
|
||||
}
|
||||
}
|
||||
} catch (e) { }
|
||||
}
|
||||
return false;
|
||||
})();
|
||||
} else if (Array.isArray(itemK)) {
|
||||
if (!itemK.includes(cond[k])) {
|
||||
return false;
|
||||
}
|
||||
} else if (itemK !== cond[k]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export function dotGetter(d: any, k: string) {
|
||||
let v = d;
|
||||
for (const kn of k.split(".")) {
|
||||
if (v && v.constructor === {}.constructor) {
|
||||
if (kn === "*") {
|
||||
v = Object.values(v);
|
||||
} else {
|
||||
v = v[kn];
|
||||
if (v === undefined) {
|
||||
v = {};
|
||||
}
|
||||
}
|
||||
} else if (Array.isArray(v)) {
|
||||
try {
|
||||
v = v[parseInt(kn)];
|
||||
if (v === undefined) {
|
||||
v = null;
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
v = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (v && v.constructor === {}.constructor && Object.keys(v).length === 0) {
|
||||
v = null;
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
export function sorter(sortBy?: string, desc?: boolean) {
|
||||
return (a: any, b: any) => {
|
||||
if (!sortBy) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const m = a[sortBy];
|
||||
const n = b[sortBy];
|
||||
|
||||
if (typeof m === typeof n) {
|
||||
if (typeof m === "string") {
|
||||
return desc ? n.localeCompare(m) : m.localeCompare(n);
|
||||
} else if (typeof m === "number") {
|
||||
return desc ? n - m : m - n;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
const typeDict = {
|
||||
"number": 1,
|
||||
"string": 2,
|
||||
"object": 3
|
||||
} as any;
|
||||
|
||||
const tM = typeDict[typeof m] || -1;
|
||||
const tN = typeDict[typeof n] || -1;
|
||||
|
||||
return desc ? tN - tM : tM - tN;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
---
|
||||
shuffle: true
|
||||
js:
|
||||
- https://patarapolw.github.io/s2speak/index.min.js?key=x
|
||||
---
|
||||
|
||||
罗马人的黄金时代已过去了。
|
||||
|
||||
--
|
||||
|
||||
- 罗 马 luó mǎ Rome, capital of Italy
|
||||
- 黄 金 时 代 huáng jīn shí dài golden age
|
||||
- 已 yǐ already; to stop
|
||||
- 过 去 guò qù (in the) past
|
||||
|
||||
---
|
||||
|
||||
黄河是中国的第二大河流。
|
||||
|
||||
--
|
||||
|
||||
- 河 流 hé liú river
|
||||
|
||||
---
|
||||
|
||||
哲学上的玄想出自实际情况。
|
||||
|
||||
--
|
||||
|
||||
- 哲 学 zhé xué philosophy
|
||||
- 玄 xuán black; mysterious
|
||||
- 想 出 xiǎng chū to figure out
|
||||
- 实 际 情 况 shí jì qíng kuàng actual circumstances
|
||||
|
||||
---
|
||||
|
||||
宇宙毫无疑问是无限的。
|
||||
|
||||
--
|
||||
|
||||
- 宇 宙 yǔ zhòu universe; cosmos
|
||||
- 毫 无 疑 问 háo wú yí wèn certainty
|
||||
- 无 限 wú xiàn unlimited
|
||||
|
||||
---
|
||||
|
||||
许多房屋被洪水冲走了。
|
||||
|
||||
--
|
||||
|
||||
- 许 多 xǔ duō many; a lot of
|
||||
- 房 屋 fáng wū house; building
|
||||
- 被 bèi quilt; by
|
||||
- 洪 水 hóng shuǐ deluge; flood
|
||||
- 冲 走 chōng zǒu to flush away
|
||||
|
||||
---
|
||||
|
||||
多么荒唐的故事!
|
||||
|
||||
--
|
||||
|
||||
- 荒 唐 huāng táng beyond belief
|
||||
- 故 事 gù shì old practice
|
||||
|
||||
---
|
||||
|
||||
我当初荒废了岁月。
|
||||
|
||||
--
|
||||
|
||||
- 当 初 dāng chū at that time
|
||||
- 荒 废 huāng fèi to abandon (cultivated fields)
|
||||
- 岁 月 suì yuè years; time
|
||||
|
||||
---
|
||||
|
||||
资产盈利能力或一些其他的价值,它们的主人。
|
||||
|
||||
--
|
||||
|
||||
- 资 产 zī chǎn property; assets
|
||||
- 盈 利 yíng lì profit; gain
|
||||
- 能 力 néng lì capability; ability
|
||||
- 或 huò maybe; perhaps; or
|
||||
- 一 些 yī xiē some; a few
|
||||
- 其 他 qí tā other; the rest
|
||||
- 价 值 jià zhí value; worth
|
||||
- 主 人 zhǔ rén master; host; owner
|
||||
|
||||
---
|
||||
|
||||
庆祝历史上的每个圣人的诞辰
|
||||
|
||||
--
|
||||
|
||||
- 庆 祝 qìng zhù to celebrate
|
||||
- 历 史 上 lì shǐ shàng historical
|
||||
- 圣 人 shèng rén saint; sage
|
||||
- 诞 辰 dàn chén birthday
|
||||
|
||||
---
|
||||
|
||||
那张CD是我儿子的。
|
||||
@@ -1,156 +0,0 @@
|
||||
---
|
||||
shuffle: true
|
||||
js:
|
||||
- https://patarapolw.github.io/s2speak/index.min.js?key=x
|
||||
---
|
||||
|
||||
一股寒流侵袭了欧洲。
|
||||
|
||||
--
|
||||
|
||||
- 寒 流 hán liú cold stream
|
||||
- 侵 袭 qīn xí to invade
|
||||
- 欧 洲 ōu zhōu Europe
|
||||
|
||||
---
|
||||
|
||||
天气非常寒冷。
|
||||
|
||||
--
|
||||
|
||||
- 寒 冷 hán lěng cold (climate)
|
||||
|
||||
---
|
||||
|
||||
寒风席卷北方。
|
||||
|
||||
--
|
||||
|
||||
- 席 卷 xí juǎn to engulf; to sweep
|
||||
- 北 方 běi fāng north
|
||||
|
||||
---
|
||||
|
||||
暑假期间,我在邮局工作。
|
||||
|
||||
--
|
||||
|
||||
- 暑 假 shǔ jià summer vacation
|
||||
- 期 间 qī jiān period of time
|
||||
- 邮 局 yóu jú post office
|
||||
|
||||
---
|
||||
|
||||
他们去了爱丁堡避暑。
|
||||
|
||||
--
|
||||
|
||||
- 爱 丁 堡 ài dīng bǎo Edinburgh, capital of Scotland
|
||||
- 避 暑 bì shǔ to be away for the summer holidays
|
||||
|
||||
---
|
||||
|
||||
然而,相反的理论往往也是真的。
|
||||
|
||||
--
|
||||
|
||||
- 然 而 rán ér however; yet; but
|
||||
- 相 反 xiāng fǎn opposite; contrary
|
||||
- 理 论 lǐ lùn theory; to argue
|
||||
- 往 往 wǎng wǎng often; frequently
|
||||
|
||||
---
|
||||
|
||||
我不能再往前走了。
|
||||
|
||||
--
|
||||
|
||||
- 往 前 wǎng qián to move forwards
|
||||
|
||||
---
|
||||
|
||||
春天和秋天,你更喜欢哪个?
|
||||
|
||||
---
|
||||
|
||||
今年中秋节是星期一吗?
|
||||
|
||||
--
|
||||
|
||||
- 中 秋 节 zhōng qiū jié the Mid-Autumn Festival on 15th of 8th lunar month
|
||||
|
||||
---
|
||||
|
||||
你有没有收据?
|
||||
|
||||
--
|
||||
|
||||
- 收 据 shōu jù receipt
|
||||
|
||||
---
|
||||
|
||||
对不起。我收回我的话。
|
||||
|
||||
--
|
||||
|
||||
- 收 回 shōu huí to regain
|
||||
|
||||
---
|
||||
|
||||
我今天收到了她的信。
|
||||
|
||||
--
|
||||
|
||||
- 收 到 shōu dào to receive
|
||||
|
||||
---
|
||||
|
||||
西藏的好水就是世界的好水。
|
||||
|
||||
--
|
||||
|
||||
- 西 藏 xī zàng Tibet
|
||||
- 世 界 shì jiè world
|
||||
|
||||
---
|
||||
|
||||
他把它藏在了门后。
|
||||
|
||||
---
|
||||
|
||||
谁在影子里藏着?
|
||||
|
||||
--
|
||||
|
||||
- 影 子 yǐng zi shadow; reflection
|
||||
|
||||
---
|
||||
|
||||
我想知道这个冬天在我们的城市会不会下雪。
|
||||
|
||||
--
|
||||
|
||||
- 城 市 chéng shì city; town
|
||||
- 下 雪 xià xuě to snow
|
||||
|
||||
---
|
||||
|
||||
冬季是一年中最冷的季节。
|
||||
|
||||
--
|
||||
|
||||
- 冬 季 dōng jì winter
|
||||
- 年 中 nián zhōng within the year
|
||||
- 季 节 jì jié time; season
|
||||
|
||||
---
|
||||
|
||||
我们为过冬储备了10吨煤。
|
||||
|
||||
--
|
||||
|
||||
- 过 冬 guò dōng to get through the winter
|
||||
- 储 备 chǔ bèi reserves
|
||||
- 吨 dūn ton (loanword)
|
||||
- 煤 méi coal
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
---
|
||||
shuffle: true
|
||||
js:
|
||||
- https://patarapolw.github.io/s2speak/index.min.js?key=x
|
||||
---
|
||||
|
||||
用闰表来测定的格林尼治时间。
|
||||
|
||||
--
|
||||
|
||||
- 测 定 cè dìng to survey and evaluate
|
||||
- 格 林 尼 治 gé lín ní zhì Greenwich (former location of Greenwich observatory, at zero longitude)
|
||||
|
||||
---
|
||||
|
||||
闰的(闰日、闰年的)在日历中加入一天或一月而使历年与太阳年相吻合的
|
||||
|
||||
--
|
||||
|
||||
- 加 入 jiā rù to become a member
|
||||
- 日 历 rì lì calendar
|
||||
- 历 年 lì nián calendar year
|
||||
- 吻 合 wěn hé to be a good fit
|
||||
|
||||
---
|
||||
|
||||
尽管现在中国人的生活越来越好了,可是还有改进的余地。
|
||||
|
||||
--
|
||||
|
||||
- 尽 管 jǐn guǎn despite; although
|
||||
- 越 来 越 yuè lái yuè more and more
|
||||
- 改 进 gǎi jìn to improve
|
||||
- 余 地 yú dì margin; leeway
|
||||
|
||||
---
|
||||
|
||||
你留在这儿,其余的人去外面
|
||||
|
||||
--
|
||||
|
||||
- 其 余 qí yú the rest; remaining
|
||||
|
||||
---
|
||||
|
||||
他和我同岁。
|
||||
|
||||
---
|
||||
|
||||
你多大岁数了?
|
||||
|
||||
---
|
||||
|
||||
法律有很多不明确的地方。
|
||||
|
||||
--
|
||||
|
||||
- 法 律 fǎ lǜ law
|
||||
- 不 明 确 bù míng què indefinite; unclear
|
||||
|
||||
---
|
||||
|
||||
嚴以律己, 寬以待人。
|
||||
|
||||
--
|
||||
|
||||
- 严 以 律 己 yǎn yǐ lǜ jǐ to be strict with oneself (idiom)
|
||||
- 宽 以 待 人 kuān yǐ dài rén to be lenient with others (idiom)
|
||||
|
||||
---
|
||||
|
||||
心里正在捉着迷藏,吕校长鞠躬请他演讲,下面一阵鼓掌。
|
||||
|
||||
--
|
||||
|
||||
- 捉 zhuō to clutch; to grab
|
||||
- 着 迷 zháo mí to be fascinated
|
||||
- 校 长 xiào zhǎng (college, university) president
|
||||
- 鞠 躬 jū gōng to bow
|
||||
- 演 讲 yǎn jiǎng lecture
|
||||
- 一 阵 yī zhèn a burst; a fit
|
||||
- 鼓 掌 gǔ zhǎng to applaud; to clap
|
||||
|
||||
---
|
||||
|
||||
他强调都市生活的便利方面。
|
||||
|
||||
--
|
||||
|
||||
- 强 调 qiáng diào to emphasize (a statement)
|
||||
- 都 市 dū shì city; metropolis
|
||||
- 生 活 shēng huó life; activity
|
||||
- 便 利 biàn lì convenient; easy
|
||||
- 方 面 fāng miàn respect; aspect
|
||||
|
||||
---
|
||||
|
||||
现今人们没有空调无法生活。
|
||||
|
||||
--
|
||||
|
||||
- 无 法 wú fǎ unable; incapable
|
||||
|
||||
---
|
||||
|
||||
众口难调。
|
||||
|
||||
---
|
||||
|
||||
我想特别强调这一点。
|
||||
|
||||
---
|
||||
|
||||
如果没有太阳,那所有生物都会死。
|
||||
|
||||
---
|
||||
|
||||
住家的阳台都对着广
|
||||
|
||||
--
|
||||
|
||||
- 阳 台 yáng tái balcony; porch
|
||||
|
||||
---
|
||||
|
||||
这间房照不到很多阳光。
|
||||
@@ -1,111 +0,0 @@
|
||||
---
|
||||
shuffle: true
|
||||
js:
|
||||
- https://patarapolw.github.io/s2speak/index.min.js?key=x
|
||||
---
|
||||
|
||||
他们俩相爱已经多年。
|
||||
|
||||
---
|
||||
|
||||
她特别地恨那个男孩。
|
||||
|
||||
---
|
||||
|
||||
听了这消息我心里很不安。
|
||||
|
||||
---
|
||||
|
||||
她的优点是临危不乱。
|
||||
|
||||
---
|
||||
|
||||
多数旧电影片都是黑白的。
|
||||
|
||||
---
|
||||
|
||||
这个墨颜色很黑。
|
||||
|
||||
---
|
||||
|
||||
冰太薄,承受不住你的重量。
|
||||
|
||||
---
|
||||
|
||||
厚厚的云把风景遮住了。
|
||||
|
||||
---
|
||||
|
||||
我饱了,一点也吃不下了。
|
||||
|
||||
---
|
||||
|
||||
这孩子不是肚子饿,就是馋嘴。
|
||||
|
||||
---
|
||||
|
||||
他的境遇很可悲。
|
||||
|
||||
---
|
||||
|
||||
大厅里充满了他们的欢笑声。
|
||||
|
||||
---
|
||||
|
||||
他的背上有一个蛇形的红斑。
|
||||
|
||||
---
|
||||
|
||||
病人服药后腹痛逐止。
|
||||
|
||||
---
|
||||
|
||||
虽然笨,可是他很刻苦。
|
||||
|
||||
---
|
||||
|
||||
他很年轻,身子骨很灵巧。
|
||||
|
||||
---
|
||||
|
||||
这本书的缺点是太长。
|
||||
|
||||
---
|
||||
|
||||
他把头发剪成时兴的短发。
|
||||
|
||||
---
|
||||
|
||||
他发誓要为父亲报仇。
|
||||
|
||||
---
|
||||
|
||||
你不像是忘恩负义的人。
|
||||
|
||||
---
|
||||
|
||||
安居乐业
|
||||
|
||||
---
|
||||
|
||||
黑名单
|
||||
|
||||
---
|
||||
|
||||
厚此薄彼
|
||||
|
||||
---
|
||||
|
||||
悲欢离合
|
||||
|
||||
---
|
||||
|
||||
笨手笨脚
|
||||
|
||||
---
|
||||
|
||||
日短夜长
|
||||
|
||||
---
|
||||
|
||||
恩将仇报
|
||||
@@ -1,172 +0,0 @@
|
||||
---
|
||||
shuffle: true
|
||||
js:
|
||||
- https://patarapolw.github.io/s2speak/index.min.js?key=x
|
||||
source: https://th.hujiang.com/new/p394824/
|
||||
---
|
||||
|
||||
调味料
|
||||
|
||||
--
|
||||
|
||||
เครื่องปรุงรส
|
||||
|
||||
---
|
||||
|
||||
沙拉油
|
||||
|
||||
--
|
||||
|
||||
น้ำมันสลัด
|
||||
|
||||
---
|
||||
|
||||
蚝油
|
||||
|
||||
--
|
||||
|
||||
น้ำมันหอย
|
||||
|
||||
---
|
||||
|
||||
面包屑
|
||||
|
||||
--
|
||||
|
||||
เศษขนมปัง
|
||||
|
||||
---
|
||||
|
||||
酦粉
|
||||
|
||||
--
|
||||
|
||||
แป้งฟู
|
||||
|
||||
---
|
||||
|
||||
调味汁
|
||||
|
||||
--
|
||||
|
||||
น้ำปรุงรสอาหาร
|
||||
|
||||
---
|
||||
|
||||
面粉
|
||||
|
||||
--
|
||||
|
||||
แป้งข้าวสาลี
|
||||
|
||||
---
|
||||
|
||||
香料
|
||||
|
||||
--
|
||||
|
||||
เครื่องเทศ
|
||||
|
||||
---
|
||||
|
||||
盐
|
||||
|
||||
--
|
||||
|
||||
เกลือ
|
||||
|
||||
---
|
||||
|
||||
胡椒
|
||||
|
||||
--
|
||||
|
||||
พริกไทย
|
||||
|
||||
---
|
||||
|
||||
蕃茄酱
|
||||
|
||||
--
|
||||
|
||||
ซอสมะเขือเทศ
|
||||
|
||||
---
|
||||
|
||||
酱油
|
||||
|
||||
--
|
||||
|
||||
ซีอิ๊ว
|
||||
|
||||
---
|
||||
|
||||
糖
|
||||
|
||||
--
|
||||
|
||||
น้ำตาล
|
||||
|
||||
---
|
||||
|
||||
辣酱
|
||||
|
||||
--
|
||||
|
||||
ซอสพริก
|
||||
|
||||
---
|
||||
|
||||
醋
|
||||
|
||||
--
|
||||
|
||||
น้ำส้ม
|
||||
|
||||
---
|
||||
|
||||
黄油
|
||||
|
||||
--
|
||||
|
||||
เนย
|
||||
|
||||
---
|
||||
|
||||
花生酱
|
||||
|
||||
--
|
||||
|
||||
เนยถั่ว
|
||||
|
||||
---
|
||||
|
||||
椰浆
|
||||
|
||||
--
|
||||
|
||||
น้ำกะทิ
|
||||
|
||||
---
|
||||
|
||||
牛奶
|
||||
|
||||
--
|
||||
|
||||
นม
|
||||
|
||||
---
|
||||
|
||||
虾酱
|
||||
|
||||
--
|
||||
|
||||
กะปิ
|
||||
|
||||
---
|
||||
|
||||
鱼露
|
||||
|
||||
--
|
||||
|
||||
น้ำปลา
|
||||
@@ -1,112 +0,0 @@
|
||||
---
|
||||
shuffle: true
|
||||
js:
|
||||
- https://patarapolw.github.io/s2speak/index.min.js?key=x
|
||||
---
|
||||
|
||||
我们应当用右手拿着坚固的盾牌去捍卫我们在中东的利益。
|
||||
|
||||
--
|
||||
|
||||
- 应 当 yīng dāng should; ought to
|
||||
- 右 手 yòu shǒu right hand
|
||||
- 拿 ná to hold; to seize
|
||||
- 坚 固 jiān gù firm; firmly; hard
|
||||
- 盾 牌 dùn pái shield; pretext
|
||||
- 捍 卫 hàn wèi to defend
|
||||
- 利 益 lì yì benefit
|
||||
|
||||
---
|
||||
|
||||
倾盆大雨劈头盖脸地浇了下来。
|
||||
|
||||
--
|
||||
|
||||
- 倾 盆 大 雨 qīng pén dà yǔ a downpour
|
||||
- 劈 头 盖 脸 pī tóu gài liǎn lit. splitting the head and covering the face (idiom)
|
||||
- 浇 jiāo to pour liquid
|
||||
- 下 来 xià lai to come down
|
||||
|
||||
---
|
||||
|
||||
我踢足球的時候弄傷了腿。
|
||||
|
||||
--
|
||||
|
||||
- 踢 tī to kick
|
||||
- 足 球 zú qiú soccer ball; soccer
|
||||
- 弄 伤 nòng shāng to bruise
|
||||
- 腿 tuǐ hip bone
|
||||
|
||||
---
|
||||
|
||||
你认为汤姆的腰围是多少?
|
||||
|
||||
--
|
||||
|
||||
- 汤 姆 tāng mǔ Tom (name)
|
||||
- 腰 围 yāo wéi waist measurement
|
||||
|
||||
---
|
||||
|
||||
一定是有人偷了你的手錶。
|
||||
|
||||
--
|
||||
|
||||
- 偷 tōu to steal; to pilfer
|
||||
- 手 表 shǒu biǎo wrist watch
|
||||
|
||||
---
|
||||
|
||||
抡锤子的工作使胳膊上的肌肉结成了一块块硬疙瘩。
|
||||
|
||||
--
|
||||
|
||||
- 抡 lūn to swing (one's arms, a heavy object)
|
||||
- 锤 子 chuí zi hammer
|
||||
- 胳 膊 gē bo arm
|
||||
- 肌 肉 jī ròu muscle; flesh
|
||||
- 结 成 jié chéng to form
|
||||
- 硬 yìng hard; stiff; strong
|
||||
- 疙 瘩 gē da swelling or lump on skin
|
||||
|
||||
---
|
||||
|
||||
吃些火腿。
|
||||
|
||||
--
|
||||
|
||||
- 些 xiē some; few; several
|
||||
- 火 腿 huǒ tuǐ ham
|
||||
|
||||
---
|
||||
|
||||
他脸上显出一副严肃的神色。
|
||||
|
||||
--
|
||||
|
||||
- 脸 liǎn face
|
||||
- 显 出 xiǎn chū to express
|
||||
- 严 肃 yán sù solemn; grave
|
||||
- 神 色 shén sè expression; look
|
||||
|
||||
---
|
||||
|
||||
鸟的翅膀相当于人的胳膊。
|
||||
|
||||
--
|
||||
|
||||
- 翅 膀 chì bǎng wing
|
||||
- 相 当 于 xiāng dāng yú equivalent to
|
||||
- 胳 膊 gē bo arm
|
||||
|
||||
---
|
||||
|
||||
这事他早就扔在脖子后边了。
|
||||
|
||||
--
|
||||
|
||||
- 早 就 zǎo jiù already at an earlier time
|
||||
- 扔 rēng to throw
|
||||
- 脖 子 bó zi neck
|
||||
- 后 边 hòu bian back; rear; behind
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"root": "."
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import Vue from 'vue';
|
||||
import Router from 'vue-router';
|
||||
import Search from './views/Search.vue';
|
||||
import Single from "./views/Single.vue";
|
||||
import About from "./views/About.vue";
|
||||
|
||||
Vue.use(Router);
|
||||
|
||||
export default new Router({
|
||||
mode: "history",
|
||||
routes: [
|
||||
{ path: '/', component: Search },
|
||||
{ path: '/tag/:tag', component: Search },
|
||||
{ path: "/p/:y/:mo/:name", component: Single },
|
||||
{ path: "/about", component: About }
|
||||
]
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
export const g = {
|
||||
q: "",
|
||||
posts: POSTS,
|
||||
config: CONFIG
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
import Vue, { VNode } from 'vue'
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
// tslint:disable no-empty-interface
|
||||
interface Element extends VNode {}
|
||||
// tslint:disable no-empty-interface
|
||||
interface ElementClass extends Vue {}
|
||||
interface IntrinsicElements {
|
||||
[elem: string]: any
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
declare module '*.vue' {
|
||||
import Vue from 'vue'
|
||||
export default Vue
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export function escapeRegExp(s: string) {
|
||||
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
||||
}
|
||||
|
||||
export function normalizeArray(it: any): any {
|
||||
if (Array.isArray(it)) {
|
||||
return it[0];
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<template lang="pug">
|
||||
b-card.p-3(v-html="content")
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Vue, Component } from "vue-property-decorator";
|
||||
import { md } from "@/plugins/render";
|
||||
|
||||
@Component
|
||||
export default class About extends Vue {
|
||||
private content: string = "";
|
||||
|
||||
public mounted() {
|
||||
this.content = md.md2html(README);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,61 @@
|
||||
<template lang="pug">
|
||||
div
|
||||
h1.tag(v-if="$route.tag") {{$route.tag}}
|
||||
div(v-if="posts && posts.length > 0")
|
||||
post(v-for="p in posts" :filename="p.filename" :is-teaser="true" :key="p.filename")
|
||||
b-pagination(v-model="page" :total-rows="count" :per-page="perPage")
|
||||
div(v-else-if="posts")
|
||||
empty
|
||||
div(v-else)
|
||||
first
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Vue, Component, Prop, Watch } from "vue-property-decorator";
|
||||
import Post from "@/layouts/Post.vue";
|
||||
import Empty from "@/layouts/Empty.vue";
|
||||
import First from "@/layouts/First.vue";
|
||||
import SearchParser from "@/plugins/search";
|
||||
import { normalizeArray } from "@/util";
|
||||
import { g } from "@/shared";
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
Post, Empty, First
|
||||
}
|
||||
})
|
||||
export default class Search extends Vue {
|
||||
private posts?: any[] = [];
|
||||
|
||||
private g = g;
|
||||
private page: number = 1;
|
||||
private count: number = 0;
|
||||
private perPage: number = 5;
|
||||
|
||||
public mounted() {
|
||||
this.page = parseInt(normalizeArray(this.$route.query.page) || "1");
|
||||
this.updatePosts();
|
||||
}
|
||||
|
||||
@Watch("g.q")
|
||||
@Watch("page")
|
||||
@Watch("$route.params")
|
||||
private async updatePosts() {
|
||||
const parser = new SearchParser();
|
||||
let ps = this.$route.params.tag
|
||||
? parser.filterPosts([this.g.q, `tag=${this.$route.params.tag}`].join(" "))
|
||||
: parser.filterPosts(this.g.q);
|
||||
|
||||
const { perPage } = g.config.posts;
|
||||
this.perPage = perPage;
|
||||
this.count = ps.length;
|
||||
|
||||
ps = ps.filter((p, i) => {
|
||||
const iPage = i / perPage;
|
||||
return iPage >= this.page - 1 && iPage < this.page;
|
||||
});
|
||||
|
||||
this.posts = ps;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,49 @@
|
||||
<template lang="pug">
|
||||
div
|
||||
div(v-if="filename")
|
||||
post(:is-teaser="false" :filename="filename")
|
||||
vue-disqus(:shortname="disqus" :identifier="$route.path")
|
||||
div(v-else="")
|
||||
empty
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Vue, Component, Prop } from "vue-property-decorator";
|
||||
import Post from "@/layouts/Post.vue";
|
||||
import Empty from "@/layouts/Empty.vue";
|
||||
import moment from "moment";
|
||||
import { g } from '../shared';
|
||||
import { dotGetter } from '../plugins/search';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
Post, Empty
|
||||
}
|
||||
})
|
||||
export default class Search extends Vue {
|
||||
private filename: string = "";
|
||||
|
||||
private disqus: string = dotGetter(g.config, "external.disqus");
|
||||
|
||||
public mounted() {
|
||||
const ps = g.posts.filter((p) => {
|
||||
const m = moment(p.date);
|
||||
if (m.format("YYYY") === this.$route.params.y && m.format("MM") === this.$route.params.mo) {
|
||||
if (p.filename
|
||||
.replace(/^.*\//, "")
|
||||
.replace(/\.[^.]+$/, "") === this.$route.params.name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!ps[0]) {
|
||||
this.filename = "";
|
||||
}
|
||||
|
||||
this.filename = ps[0].filename;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,54 +0,0 @@
|
||||
# Cogear.JS theme **blog**
|
||||
|
||||
|
||||

|
||||
|
||||
Theme with blog posts on the home page.
|
||||
|
||||
Simple responsive design. Posts tags support with navigation and pagination.
|
||||
|
||||
# Installation
|
||||
|
||||
You need to install [`cogear-plugin-blog`](https://github.com/codemotion/cogear-plugin-blog) to use this theme.
|
||||
|
||||
Go to the site folder and install theme with command:
|
||||
```bash
|
||||
yarn add cogear-theme-blog
|
||||
# or
|
||||
npm install cogear-theme-blog
|
||||
```
|
||||
|
||||
After that you need to edit your site `config.yaml` to set up installed theme.
|
||||
|
||||
```yaml
|
||||
theme: blog
|
||||
```
|
||||
|
||||
# Configuration
|
||||
|
||||
Use `cogear-plugin-blog` options in `./config.yaml`:
|
||||
``` yaml
|
||||
title: Blog | Cogear.JS – modern static websites generator
|
||||
description:
|
||||
keywords:
|
||||
theme: blog
|
||||
blog:
|
||||
index: "" # Provide posts to index page
|
||||
regex: "^p/"
|
||||
tagUri: 'tag'
|
||||
perPage: 5
|
||||
pages:
|
||||
^p/:
|
||||
layout: post
|
||||
author:
|
||||
login: User
|
||||
avatar: 'https://www.gravatar.com/avatar/400caf343d3bab57ab93f63e21a12be7?s=24'
|
||||
link: https://cogearjs.org
|
||||
```
|
||||
# Usage
|
||||
|
||||
You may also want to install in with [`cogear-preset-blog`](https://github.com/codemotion/cogear-preset-blog).
|
||||
|
||||
# Docs
|
||||
|
||||
More info: [https://cogearjs.org/docs/themes](https://cogearjs.org/docs/themes).
|
||||
@@ -1,13 +0,0 @@
|
||||
article.post
|
||||
header: h1 Write a post!
|
||||
.post-content
|
||||
p Go to <code>./src/pages/p</code> folder and create your first post, creating <code>first-post.md</code> file filled with the following content:
|
||||
pre.
|
||||
---
|
||||
title: First post
|
||||
tags:
|
||||
- news
|
||||
---
|
||||
# This is my first post!
|
||||
p Any questions? More info: <a href="https://github.com/codemotion/cogear-template-blog">https://github.com/codemotion/cogear-template-blog</a>
|
||||
a(href="https://cogearjs.org"): img(src="https://cogearjs.org/images/cogearjs.jpg")
|
||||
@@ -1,13 +0,0 @@
|
||||
head
|
||||
title=title
|
||||
link(rel="shortcut icon" href=`${cogear.baseUrl}favicon.ico` type="image/png")
|
||||
body
|
||||
.navbar
|
||||
.container
|
||||
.brand
|
||||
a(href="/") Blog
|
||||
if cogear.blog.tags
|
||||
ul
|
||||
each t in cogear.blog.tags
|
||||
- tName = t[0].toUpperCase() + t.slice(1)
|
||||
li: a(href=`/tags/${t}`)= tName
|
||||
@@ -1,23 +0,0 @@
|
||||
mixin post(post)
|
||||
.card.p-2.mb-3.post(class=`${post.teaser ? 'teaser' : 'full'}`)
|
||||
.post-meta.mb-3
|
||||
.float-right
|
||||
+date(post.date)
|
||||
.post-meta--author
|
||||
a(href=`${post.author.link}`): img.mr-3(src=`${post.author.avatar}`)
|
||||
a(href=`${post.author.link}`)= post.author.login
|
||||
header
|
||||
if post.teaser
|
||||
h2: a(href=`${cogear.baseUrl}${post.uri}`)= post.title
|
||||
else
|
||||
h1= post.title
|
||||
.post-content!= post.teaser ? post.teaser : post.content
|
||||
if !post.teaser
|
||||
ul.mt-3.pagination
|
||||
li.page-item
|
||||
a.page-link(href="javascript: history.back()") ← Back
|
||||
mixin date(date)
|
||||
- let months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
|
||||
- let d = new Date(date)
|
||||
- month = months[d.getMonth()]
|
||||
time= `${month} ${d.getDate()}`
|
||||
@@ -1,21 +0,0 @@
|
||||
extends layout.pug
|
||||
block head
|
||||
if tag
|
||||
- tagName = tag[0].toUpperCase() + tag.slice(1)
|
||||
- title= `${tagName} | ${title}`
|
||||
if paginator && paginator.current > 1
|
||||
- title = `Page ${paginator.current} | ${title}`
|
||||
title= title
|
||||
block content
|
||||
if tag
|
||||
h1.tag= tagName
|
||||
//- if content
|
||||
//- article.page!= content
|
||||
if posts
|
||||
- let now = new Date()
|
||||
each post in posts
|
||||
if new Date(post.date) < now
|
||||
+post(post)
|
||||
include paginator
|
||||
else
|
||||
include includes/empty
|
||||
@@ -1,34 +0,0 @@
|
||||
include includes/mixins
|
||||
head
|
||||
block head
|
||||
title= title
|
||||
meta(content="width=device-width, height=device-height, initial-scale=1, minimum-scale=1.0, maximum-scale=1" name="viewport")
|
||||
meta(charset="UTF-8")
|
||||
//- link(rel="shortcut icon" href=`${cogear.baseUrl}favicon.png` type="image/png")
|
||||
link(rel="apple-touch-icon" sizes="180x180" href=`/apple-touch-icon.png`)
|
||||
link(rel="icon" type="image/png" sizes="32x32" href=`/favicon-32x32.png`)
|
||||
link(rel="icon" type="image/png" sizes="16x16" href=`/favicon-16x16.png`)
|
||||
link(rel="manifest" href=`/site.manifest`)
|
||||
link(rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css' integrity='sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T' crossorigin='anonymous')
|
||||
body
|
||||
nav.navbar.navbar-dark.bg-success.navbar-expand-lg
|
||||
a.ml-3.navbar-brand(href="/")= cogear.config.blog.title || "BLOG"
|
||||
|
||||
button.navbar-toggler(data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false")
|
||||
span.navbar-toggler-icon
|
||||
.collapse.navbar-collapse#navbarSupportedContent
|
||||
if cogear.config.blog.tags
|
||||
ul.navbar-nav.mr-auto
|
||||
each t in cogear.config.blog.tags
|
||||
- tName = t[0].toUpperCase() + t.slice(1)
|
||||
li.nav-item: a.nav-link(href=`/tag/${t}`)= tName
|
||||
.container.mt-3
|
||||
.row
|
||||
.col-lg-8.col-md-12
|
||||
block content
|
||||
.col-lg-4.col-md-12
|
||||
include sidebar
|
||||
|
||||
script(src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.slim.min.js')
|
||||
script(src='https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js' integrity='sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1' crossorigin='anonymous')
|
||||
script(src='https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js' integrity='sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM' crossorigin='anonymous')
|
||||
@@ -1,23 +0,0 @@
|
||||
if paginator
|
||||
nav.mt-3
|
||||
ul.pagination
|
||||
if paginator.prev !== null
|
||||
li.page-item
|
||||
a.page-link(href=`${cogear.baseUrl}${paginator.prev}`) ← Prev
|
||||
- let page = 1
|
||||
while page <= paginator.total
|
||||
- let range = 2
|
||||
if page == 1 || page == paginator.total || (page >= paginator.current - range && page <= paginator.current + range)
|
||||
if page == paginator.current
|
||||
li.page-item.active
|
||||
a.page-link(href=`${paginator.baseUri}/${page}`)= page
|
||||
else
|
||||
li.page-item
|
||||
a.page-link(href=`${paginator.baseUri}/${page}`)= page
|
||||
else if paginator.current < paginator.total - range && page == paginator.total - 1 || paginator.current > 1+range && page == range
|
||||
li.page-item
|
||||
a.page-link.ellipsis …
|
||||
- page=page+1
|
||||
if paginator.next !== null
|
||||
li.page-item
|
||||
a.page-link(href=`/${paginator.next}`) Next →
|
||||
@@ -1,12 +0,0 @@
|
||||
extends layout.pug
|
||||
block content
|
||||
+post({
|
||||
title:title,
|
||||
teaser: false,
|
||||
tags:tags,
|
||||
content:content,
|
||||
date:date,
|
||||
author: author
|
||||
})
|
||||
|
||||
#disqus_thread
|
||||
@@ -1,9 +0,0 @@
|
||||
aside.card.p-3
|
||||
block sidebar
|
||||
:markdown-it
|
||||
#### Record my journey to Chinese by [@polvcode](https://polvcode.dev)
|
||||
|
||||
Highlight text and press "s" to speak.
|
||||
|
||||
a.twitter-timeline(href="https://twitter.com/ChinesePol" data-height="600") Tweets by ChinesePol
|
||||
script(async src="https://platform.twitter.com/widgets.js" charset="utf-8")
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 62 KiB |
@@ -1 +0,0 @@
|
||||
import "./theme.scss";
|
||||
@@ -1,7 +0,0 @@
|
||||
.post {
|
||||
.post-meta {
|
||||
img {
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"importHelpers": true,
|
||||
"moduleResolution": "node",
|
||||
"experimentalDecorators": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"types": [
|
||||
"webpack-env",
|
||||
"mocha"
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
},
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"tests/**/*.ts",
|
||||
"tests/**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
require = require("esm")(module)
|
||||
const glob = require("fast-glob");
|
||||
const fs = require("fs");
|
||||
const { pugFilters } = require("./src/plugins/render");
|
||||
const matter = require("gray-matter");
|
||||
const yaml = require("js-yaml");
|
||||
|
||||
function configureWebpack(webpackConfig) {
|
||||
for (const rule of webpackConfig.module.rules) {
|
||||
if (rule.use) {
|
||||
for (const use of rule.use) {
|
||||
if (/pug/.test(use.loader)) {
|
||||
use.options = {filters: pugFilters};
|
||||
}
|
||||
}
|
||||
} else if (rule.oneOf) {
|
||||
for (const oneOf of rule.oneOf) {
|
||||
if (oneOf.use) {
|
||||
for (const use of oneOf.use) {
|
||||
if (/pug/.test(use.loader)) {
|
||||
use.options = {filters: pugFilters};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(webpackConfig.devServer || {}).port = 8000;
|
||||
}
|
||||
|
||||
function defineWebpack(webpackConfig, def) {
|
||||
webpackConfig.plugin("define").tap((args) => {
|
||||
args[0] = {
|
||||
...args[0],
|
||||
...def
|
||||
}
|
||||
return args;
|
||||
});
|
||||
}
|
||||
|
||||
async function generateDefinitions() {
|
||||
return {
|
||||
POSTS: JSON.stringify(fs.existsSync("public/posts")
|
||||
? (await glob("public/posts/**/*.{md,markdown,pug,html}")).map((f) => {
|
||||
const { data, content } = matter(fs.readFileSync(f, "utf8"));
|
||||
return {
|
||||
...data,
|
||||
teaser: content.split("===")[0],
|
||||
filename: f
|
||||
}
|
||||
}) : []),
|
||||
CONFIG: JSON.stringify(yaml.safeLoad(fs.readFileSync("config.yaml", "utf8"))),
|
||||
README: JSON.stringify(fs.readFileSync("README.md", "utf8"))
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = (api, options) => {
|
||||
api.registerCommand('build:async', async (args) => {
|
||||
const def = await generateDefinitions();
|
||||
|
||||
api.configureWebpack(configureWebpack);
|
||||
api.chainWebpack((webpackConfig) => {
|
||||
defineWebpack(webpackConfig, def);
|
||||
});
|
||||
|
||||
await api.service.run('build', args)
|
||||
}),
|
||||
api.registerCommand('serve:async', async (args) => {
|
||||
const def = await generateDefinitions();
|
||||
|
||||
api.configureWebpack(configureWebpack);
|
||||
api.chainWebpack((webpackConfig) => {
|
||||
defineWebpack(webpackConfig, def);
|
||||
});
|
||||
|
||||
await api.service.run('serve', args)
|
||||
})
|
||||
}
|
||||
|
||||
module.exports.defaultModes = {
|
||||
'build:async': 'production',
|
||||
'serve:async': 'development'
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
publicPath: "/"
|
||||
}
|
||||