add post and fix bugs, also use external
This commit is contained in:
1 parent
39d6b337e6
commit
6fa1f68a6f
17 files changed
+209
-454
No files matched your search
Generated
+15
-5
@@ -1040,8 +1040,8 @@
|
|||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "12.7.2",
|
"version": "12.7.2",
|
||||||
"resolved": "https://registry.npm.taobao.org/@types/node/download/@types/node-12.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.2.tgz",
|
||||||
"integrity": "sha1-xOY69eiCPOnMPws097mYwhcfDEQ="
|
"integrity": "sha512-dyYO+f6ihZEtNPDcWNR1fkoTDf3zAK3lAABDze3mz6POyIercH0lEUawUFXlG8xaQZmm1yEBON/4TsYv/laDYg=="
|
||||||
},
|
},
|
||||||
"@types/normalize-package-data": {
|
"@types/normalize-package-data": {
|
||||||
"version": "2.4.0",
|
"version": "2.4.0",
|
||||||
@@ -6306,9 +6306,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"indented-filter": {
|
"indented-filter": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/indented-filter/-/indented-filter-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/indented-filter/-/indented-filter-0.1.1.tgz",
|
||||||
"integrity": "sha512-usPQg1fmF5JOAjg3VKHrhd7gcXaN4PuCIeyLf88/AnxZIAhys9ruuPwB2ww40eHKcz72+mMSg7Z/GVO2V5WXHQ=="
|
"integrity": "sha512-qOAYFXYHxMClzpXN95wgW/Myy7j332KEWNNl35boaAcU3tL1HPcLQLCLClzmTuyWG4kpmDk5Kps/Wz0u7paUNg=="
|
||||||
},
|
},
|
||||||
"indexes-of": {
|
"indexes-of": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@@ -9484,6 +9484,16 @@
|
|||||||
"integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
|
"integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"q2filter": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/q2filter/-/q2filter-0.1.2.tgz",
|
||||||
|
"integrity": "sha512-FIYV/QBh5ECbuRowIK4NNNkjLNzHJ9kO3654dg1jxVlQ5UAy8RTqmTlioYHeJ8qeg2+fWDGbesiNO3mpYbFXHQ==",
|
||||||
|
"requires": {
|
||||||
|
"@types/uuid": "^3.4.5",
|
||||||
|
"moment": "^2.24.0",
|
||||||
|
"uuid": "^3.3.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"qs": {
|
"qs": {
|
||||||
"version": "6.5.2",
|
"version": "6.5.2",
|
||||||
"resolved": "https://registry.npm.taobao.org/qs/download/qs-6.5.2.tgz",
|
"resolved": "https://registry.npm.taobao.org/qs/download/qs-6.5.2.tgz",
|
||||||
|
|||||||
+3
-1
@@ -10,6 +10,7 @@
|
|||||||
"deploy": "npm run build && gh-pages -d dist"
|
"deploy": "npm run build && gh-pages -d dist"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/node": "^12.7.2",
|
||||||
"@types/uuid": "^3.4.5",
|
"@types/uuid": "^3.4.5",
|
||||||
"bootstrap-vue": "^2.0.0-rc.19",
|
"bootstrap-vue": "^2.0.0-rc.19",
|
||||||
"core-js": "^2.6.5",
|
"core-js": "^2.6.5",
|
||||||
@@ -18,9 +19,10 @@
|
|||||||
"gray-matter": "^4.0.2",
|
"gray-matter": "^4.0.2",
|
||||||
"hyperpug": "^0.1.7",
|
"hyperpug": "^0.1.7",
|
||||||
"hyperscript": "^2.0.2",
|
"hyperscript": "^2.0.2",
|
||||||
"indented-filter": "^0.1.0",
|
"indented-filter": "^0.1.1",
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
"moment": "^2.24.0",
|
"moment": "^2.24.0",
|
||||||
|
"q2filter": "^0.1.2",
|
||||||
"showdown": "^1.9.0",
|
"showdown": "^1.9.0",
|
||||||
"uuid": "^3.3.3",
|
"uuid": "^3.3.3",
|
||||||
"vue": "^2.6.10",
|
"vue": "^2.6.10",
|
||||||
|
|||||||
+13
-12
@@ -4,22 +4,23 @@ date: "2019-08-10"
|
|||||||
tag:
|
tag:
|
||||||
- 千字文
|
- 千字文
|
||||||
- 汉字
|
- 汉字
|
||||||
|
- essay
|
||||||
---
|
---
|
||||||
|
|
||||||
1
|
1
|
||||||
天地玄黃,宇宙洪荒。
|
^^to(url="/p" filename="public/posts/2019/08/千字文1.1.pug") 天地玄黃,宇宙洪荒。
|
||||||
日月盈昃,辰宿列張。
|
^^to(url="/p" filename="public/posts/2019/08/千字文1.2.pug") 日月盈昃,辰宿列張。
|
||||||
寒來暑往,秋收冬藏。
|
^^to(url="/p" filename="public/posts/2019/08/千字文1.3.pug") 寒來暑往,秋收冬藏。
|
||||||
閏餘成歲,律呂調陽。
|
^^to(url="/p" filename="public/posts/2019/08/千字文1.4.pug") 閏餘成歲,律呂調陽。
|
||||||
雲騰致雨,露結為霜。
|
^^to(url="/p" filename="public/posts/2019/08/千字文1.5.pug") 雲騰致雨,露結為霜。
|
||||||
金生麗水,玉出崑岡。
|
^^to(url="/p" filename="public/posts/2019/08/千字文1.6.pug") 金生麗水,玉出崑岡。
|
||||||
劍號巨闕,珠稱夜光。
|
^^to(url="/p" filename="public/posts/2019/08/千字文1.7.pug") 劍號巨闕,珠稱夜光。
|
||||||
果珍李柰,菜重芥薑。
|
^^to(url="/p" filename="public/posts/2019/08/千字文1.8.pug") 果珍李柰,菜重芥薑。
|
||||||
海咸河淡,鱗潛羽翔。
|
^^to(url="/p" filename="public/posts/2019/08/千字文1.9.pug") 海咸河淡,鱗潛羽翔。
|
||||||
2
|
2
|
||||||
龍師火帝,鳥官人皇。
|
^^to(url="/p" filename="public/posts/2019/08/千字文2.1.pug") 龍師火帝,鳥官人皇。
|
||||||
始制文字,乃服衣裳。
|
^^to(url="/p" filename="public/posts/2019/08/千字文2.2.pug") 始制文字,乃服衣裳。
|
||||||
推位讓國,有虞陶唐。
|
^^to(url="/p" filename="public/posts/2019/08/千字文2.3.pug") 推位讓國,有虞陶唐。
|
||||||
弔民伐罪,周發殷湯。
|
弔民伐罪,周發殷湯。
|
||||||
坐朝問道,垂拱平章。
|
坐朝問道,垂拱平章。
|
||||||
愛育黎首,臣伏戎羌。
|
愛育黎首,臣伏戎羌。
|
||||||
|
|||||||
@@ -77,7 +77,6 @@ tag:
|
|||||||
斜阳 (斜陽) | xiéyáng | setting sun
|
斜阳 (斜陽) | xiéyáng | setting sun
|
||||||
朝阳 (朝陽) | cháoyáng | to be exposed to the sun / in a position facing the sun
|
朝阳 (朝陽) | cháoyáng | to be exposed to the sun / in a position facing the sun
|
||||||
朝阳 (朝陽) | zhāoyáng | the morning sun
|
朝阳 (朝陽) | zhāoyáng | the morning sun
|
||||||
HSK 4
|
|
||||||
style.
|
style.
|
||||||
table td:first-child {
|
table td:first-child {
|
||||||
width: 10em;
|
width: 10em;
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
---
|
||||||
|
title: "千字文 - 2/3"
|
||||||
|
date: "2019-08-23"
|
||||||
|
tag:
|
||||||
|
- 千字文
|
||||||
|
- 汉字
|
||||||
|
---
|
||||||
|
|
||||||
|
.w-100.mt-3
|
||||||
|
h3.text-center 推位讓國,有虞陶唐。
|
||||||
|
.w-100
|
||||||
|
figure.d-inline-block
|
||||||
|
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/25512.gif" height=100)
|
||||||
|
figcaption.text-center tui1
|
||||||
|
figure.d-inline-block
|
||||||
|
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/20301.gif" height=100)
|
||||||
|
figcaption.text-center wei4
|
||||||
|
figure.d-inline-block
|
||||||
|
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/35731.gif" height=100)
|
||||||
|
figcaption.text-center rang4
|
||||||
|
figure.d-inline-block
|
||||||
|
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/22283.gif" height=100)
|
||||||
|
figcaption.text-center guo2
|
||||||
|
.w-100
|
||||||
|
figure.d-inline-block
|
||||||
|
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/26377.gif" height=100)
|
||||||
|
figcaption.text-center you3
|
||||||
|
figure.d-inline-block
|
||||||
|
.hanzi-img 虞
|
||||||
|
figcaption.text-center fu2
|
||||||
|
figure.d-inline-block
|
||||||
|
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/38518.gif" height=100)
|
||||||
|
figcaption.text-center tao2
|
||||||
|
figure.d-inline-block
|
||||||
|
img(src="https://www.mdbg.net/chinese/rsc/img/stroke_anim/21776.gif" height=100)
|
||||||
|
figcaption.text-center tang2
|
||||||
|
|
||||||
|
===
|
||||||
|
|
||||||
|
.w-100.mt-3
|
||||||
|
:markdown
|
||||||
|
推荐 (推薦) | tuījiàn | to recommend / recommendation
|
||||||
|
推广 (推廣) | tuīguǎng | to extend / to spread / to popularize / generalization / promotion (of a product etc)
|
||||||
|
推动 (推動) | tuīdòng | to push (for acceptance of a plan) / to push forward / to promote / to actuate / CL: 個|个
|
||||||
|
推进 (推進) | tuījìn | to impel / to carry forward / to push on / to advance / to drive forward
|
||||||
|
推 | tuī | to push / to cut / to refuse / to reject / to decline / to shirk (responsibility) / to put off / to delay / to push forward / to nominate / to elect / massage
|
||||||
|
推出 | tuīchū | to push out / to release / to launch / to publish / to recommend
|
||||||
|
推理 | tuīlǐ | reasoning / inference / to infer / to deduce
|
||||||
|
推介 | tuījiè | promotion / to promote / to introduce and recommend
|
||||||
|
推行 | tuīxíng | to put into effect / to carry out
|
||||||
|
单位 (單位) | dānwèi | unit (of measure) / unit (group of people as a whole) / work unit (place of employment, esp. in the PRC prior to economic reform) / CL: 個|个
|
||||||
|
地位 | dìwèi | position / status / place / CL: 個|个
|
||||||
|
位置 | wèizhi | position / place / seat / CL: 個|个
|
||||||
|
位 | wèi | position / location / place / seat / classifier for people (honorific) / classifier for binary bits (e.g. 十六位 16-bit or 2 bytes) / (physics) potential
|
||||||
|
定位 | dìngwèi | to orientate / to position / to categorize (as) / to characterize (as) / positioning / position / niche
|
||||||
|
学位 (學位) | xuéwèi | academic degree / place in school
|
||||||
|
岗位 (崗位) | gǎngwèi | a post / a job
|
||||||
|
位于 (位於) | wèiyú | to be located at / to be situated at / to lie
|
||||||
|
各位 | gèwèi | everybody / all (guests, colleagues etc) / all of you
|
||||||
|
职位 (職位) | zhíwèi | post / office / position
|
||||||
|
方位 | fāngwèi | direction / points of the compass / bearing / position / azimuth
|
||||||
|
让 (讓) | ràng | to yield / to permit / to let sb do sth / to have sb do sth / to make sb (feel sad etc) / by (indicates the agent in a passive clause, like 被)
|
||||||
|
转让 (轉讓) | zhuǎnràng | to transfer (ownership, rights etc)
|
||||||
|
让球 (讓球) | ràngqiú | to concede points (in a game)
|
||||||
|
出让 (出讓) | chūràng | to transfer (one's property or rights to sb else)
|
||||||
|
礼让 (禮讓) | lǐràng | to show consideration for (others) / to yield to (another vehicle etc) / courtesy / comity
|
||||||
|
让步 (讓步) | ràngbù | to concede / to give in / to yield / a concession / (linguistics) concessive
|
||||||
|
避让 (避讓) | bìràng | to avoid / to yield (in traffic) / to get out of the way
|
||||||
|
国际 (國際) | guójì | international
|
||||||
|
国家 (國家) | guójiā | country / nation / state / CL: 個|个
|
||||||
|
国内 (國內) | guónèi | domestic / internal (to a country) / civil
|
||||||
|
中国 (中國) | Zhōngguó | China
|
||||||
|
王国 (王國) | wángguó | kingdom / realm
|
||||||
|
开国 (開國) | kāiguó | to found a state / to open a closed country
|
||||||
|
版权所有 (版權所有) | bǎnquánsuǒyǒu | all rights reserved (copyright statement)
|
||||||
|
有限 | yǒuxiàn | limited / finite
|
||||||
|
有限公司 | yǒuxiàngōngsī | limited company / corporation
|
||||||
|
拥有 (擁有) | yōngyǒu | to have / to possess
|
||||||
|
虞 | yú | to expect / to anticipate / to worry / to cheat
|
||||||
|
尔虞我诈 (爾虞我詐) | ěryúwǒzhà | lit. you hoodwink me and I cheat you (idiom); fig. mutual deception / each tries to outwit the other / dog eats dog and devil take the hindmost
|
||||||
|
无虞 (無虞) | wúyú | not to be worried about / all taken care of
|
||||||
|
安全无虞 (安全無虞) | ānquánwúyú | safe and secure
|
||||||
|
有效 | yǒuxiào | effective / in effect / valid
|
||||||
|
所有 | suǒyǒu | all / to have / to possess / to own
|
||||||
|
陶瓷 | táocí | pottery and porcelain / ceramics
|
||||||
|
陶 | táo | pottery / pleased
|
||||||
|
陶艺 (陶藝) | táoyì | ceramic art
|
||||||
|
陶醉 | táozuì | to be infatuated with / to be drunk with / to be enchanted with / to revel in
|
||||||
|
熏陶 (薰陶) | xūntáo | to seep in / to influence / to nurture / influence / training
|
||||||
|
陶器 | táoqì | pottery
|
||||||
|
陶冶 | táoyě | lit. to fire pots and smelt metal / fig. to educate
|
||||||
|
唐 | Táng | Tang dynasty (618-907) / surname Tang
|
||||||
|
唐 | táng | to exaggerate / empty / in vain / old variant of 螗
|
||||||
|
唐诗 (唐詩) | Tángshī | Tang poetry / a Tang poem
|
||||||
|
唐人街 | Tángrén Jiē | Chinatown / CL: 條|条, 座
|
||||||
|
荒唐 | huāngtáng | beyond belief / preposterous / absurd / intemperate / dissipated
|
||||||
|
style.
|
||||||
|
table td:first-child {
|
||||||
|
width: 10em;
|
||||||
|
}
|
||||||
@@ -32,3 +32,10 @@ export default class App extends Vue {
|
|||||||
private g = g;
|
private g = g;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.b-link {
|
||||||
|
color: blue;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -90,7 +90,6 @@ export default class Post extends Vue {
|
|||||||
get content() {
|
get content() {
|
||||||
const { content } = this.matter;
|
const { content } = this.matter;
|
||||||
const m = /^([^]+)\s+===\s+([^]+)$/.exec(content || "");
|
const m = /^([^]+)\s+===\s+([^]+)$/.exec(content || "");
|
||||||
console.log(m)
|
|
||||||
if (m) {
|
if (m) {
|
||||||
return this.parseContent(m[1] + (m[2] || ""));
|
return this.parseContent(m[1] + (m[2] || ""));
|
||||||
}
|
}
|
||||||
@@ -99,7 +98,6 @@ export default class Post extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async mounted() {
|
public async mounted() {
|
||||||
console.log(this.url);
|
|
||||||
this.matter = matter(await (await fetch(this.url)).text());
|
this.matter = matter(await (await fetch(this.url)).text());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ div
|
|||||||
|
|
||||||
Highlight text and press "s" to speak.
|
Highlight text and press "s" to speak.
|
||||||
a.twitter-timeline(href="https://twitter.com/ChinesePol" data-height="600") Tweets by ChinesePol
|
a.twitter-timeline(href="https://twitter.com/ChinesePol" data-height="600") Tweets by ChinesePol
|
||||||
b-card
|
b-card.mb-3
|
||||||
h3 Tag cloud
|
h3 Tag cloud
|
||||||
span.mr-3(v-for="t in tags" :key="t.name" :class="t.class")
|
span.mr-3(v-for="t in tags" :key="t.name" :class="t.class")
|
||||||
b-link(:to="'/tag/' + t.name") {{t.name}}
|
b-link(:to="'/tag/' + t.name") {{t.name}}
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
import { g } from "@/shared";
|
import { g } from "@/shared";
|
||||||
import { dotGetter } from './search';
|
import { dotGetter } from "q2filter";
|
||||||
|
|
||||||
declare const dataLayer: any[];
|
declare const dataLayer: any[];
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import showdown from "showdown";
|
import showdown from "showdown";
|
||||||
import { slideExt, simpleTableExt } from "./showdown-ext";
|
import { slideExt, simpleTableExt, toExt } from "./showdown-ext";
|
||||||
|
|
||||||
export class Markdown {
|
export class Markdown {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.converter = new showdown.Converter({
|
this.converter = new showdown.Converter({
|
||||||
parseImgDimensions: true,
|
parseImgDimensions: true,
|
||||||
extensions: [simpleTableExt, slideExt]
|
extensions: [simpleTableExt, slideExt, toExt]
|
||||||
});
|
});
|
||||||
this.converter.setFlavor("github");
|
this.converter.setFlavor("github");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
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});
|
|
||||||
})
|
|
||||||
});
|
|
||||||
})
|
|
||||||
@@ -1,396 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -42,3 +42,20 @@ export const slideExt = {
|
|||||||
return a.outerHTML;
|
return a.outerHTML;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const toExt = {
|
||||||
|
type: "lang",
|
||||||
|
filter: createIndentedFilter("^^to", (s, attrs) => {
|
||||||
|
let {url, ...params} = attrs;
|
||||||
|
let q = []
|
||||||
|
for (const [k, v] of Object.entries(params)) {
|
||||||
|
q.push(`${k}=${encodeURIComponent(v)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q.length > 0) {
|
||||||
|
url = `${url}?${q.join("&")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return h("span.b-link", {attrs: {to: url}}, s).outerHTML;
|
||||||
|
})
|
||||||
|
}
|
||||||
+14
-1
@@ -6,12 +6,25 @@ import About from "./views/About.vue";
|
|||||||
|
|
||||||
Vue.use(Router);
|
Vue.use(Router);
|
||||||
|
|
||||||
export default new Router({
|
const router = new Router({
|
||||||
mode: "history",
|
mode: "history",
|
||||||
routes: [
|
routes: [
|
||||||
{ path: '/', component: Search },
|
{ path: '/', component: Search },
|
||||||
{ path: '/tag/:tag', component: Search },
|
{ path: '/tag/:tag', component: Search },
|
||||||
|
{ path: "/p", component: Single },
|
||||||
{ path: "/p/:y/:mo/:name", component: Single },
|
{ path: "/p/:y/:mo/:name", component: Single },
|
||||||
{ path: "/about", component: About }
|
{ path: "/about", component: About }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.addEventListener("click", (ev) => {
|
||||||
|
const { target } = ev;
|
||||||
|
if (target instanceof HTMLSpanElement && target.matches(".b-link")) {
|
||||||
|
const to = target.getAttribute("to")
|
||||||
|
if (to) {
|
||||||
|
router.push(to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
+19
-6
@@ -15,9 +15,9 @@ import { Vue, Component, Prop, Watch } from "vue-property-decorator";
|
|||||||
import Post from "@/layouts/Post.vue";
|
import Post from "@/layouts/Post.vue";
|
||||||
import Empty from "@/layouts/Empty.vue";
|
import Empty from "@/layouts/Empty.vue";
|
||||||
import First from "@/layouts/First.vue";
|
import First from "@/layouts/First.vue";
|
||||||
import SearchParser from "@/plugins/search";
|
|
||||||
import { normalizeArray } from "@/util";
|
import { normalizeArray } from "@/util";
|
||||||
import { g } from "@/shared";
|
import { g } from "@/shared";
|
||||||
|
import QParser from "q2filter";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {
|
components: {
|
||||||
@@ -39,12 +39,25 @@ export default class Search extends Vue {
|
|||||||
|
|
||||||
@Watch("g.q")
|
@Watch("g.q")
|
||||||
@Watch("page")
|
@Watch("page")
|
||||||
@Watch("$route.params")
|
@Watch("$route.params.tag")
|
||||||
private async updatePosts() {
|
private async updatePosts() {
|
||||||
const parser = new SearchParser();
|
const parser = new QParser({
|
||||||
let ps = this.$route.params.tag
|
isString: ["title", "author", "tag"],
|
||||||
? parser.filterPosts([this.g.q, `tag=${this.$route.params.tag}`].join(" "))
|
isDate: ["date"],
|
||||||
: parser.filterPosts(this.g.q);
|
filters: {
|
||||||
|
"is:reversed": (items) => {
|
||||||
|
return items.reverse();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sortBy: "date",
|
||||||
|
desc: true
|
||||||
|
});
|
||||||
|
const q: string [] = [this.g.q];
|
||||||
|
if (this.$route.params.tag) {
|
||||||
|
q.push(`tag=${this.$route.params.tag}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ps = parser.filter(POSTS, q.join(" "));
|
||||||
|
|
||||||
const { perPage } = g.config.posts;
|
const { perPage } = g.config.posts;
|
||||||
this.perPage = perPage;
|
this.perPage = perPage;
|
||||||
|
|||||||
+15
-4
@@ -1,19 +1,19 @@
|
|||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
div
|
div
|
||||||
div(v-if="filename")
|
div(v-if="filename")
|
||||||
post(:is-teaser="false" :filename="filename")
|
post(:is-teaser="false" :filename="filename" :key="filename")
|
||||||
vue-disqus(:shortname="disqus" :identifier="$route.path")
|
vue-disqus(:shortname="disqus" :identifier="$route.path")
|
||||||
div(v-else="")
|
div(v-else="")
|
||||||
empty
|
empty
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Vue, Component, Prop } from "vue-property-decorator";
|
import { Vue, Component, Prop, Watch } from "vue-property-decorator";
|
||||||
import Post from "@/layouts/Post.vue";
|
import Post from "@/layouts/Post.vue";
|
||||||
import Empty from "@/layouts/Empty.vue";
|
import Empty from "@/layouts/Empty.vue";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { g } from '../shared';
|
import { g } from '../shared';
|
||||||
import { dotGetter } from '../plugins/search';
|
import { dotGetter } from "q2filter";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {
|
components: {
|
||||||
@@ -26,7 +26,17 @@ export default class Search extends Vue {
|
|||||||
private disqus: string = dotGetter(g.config, "external.disqus");
|
private disqus: string = dotGetter(g.config, "external.disqus");
|
||||||
|
|
||||||
public mounted() {
|
public mounted() {
|
||||||
|
this.updateFilename();
|
||||||
|
}
|
||||||
|
@Watch("$route.path")
|
||||||
|
public updateFilename() {
|
||||||
const ps = g.posts.filter((p) => {
|
const ps = g.posts.filter((p) => {
|
||||||
|
const { filename } = this.$route.query;
|
||||||
|
if (filename) {
|
||||||
|
if (encodeURI(p.filename) === encodeURI(filename as string)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
const m = moment(p.date);
|
const m = moment(p.date);
|
||||||
if (m.format("YYYY") === this.$route.params.y && m.format("MM") === this.$route.params.mo) {
|
if (m.format("YYYY") === this.$route.params.y && m.format("MM") === this.$route.params.mo) {
|
||||||
if (p.filename
|
if (p.filename
|
||||||
@@ -35,6 +45,7 @@ export default class Search extends Vue {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
@@ -43,7 +54,7 @@ export default class Search extends Vue {
|
|||||||
this.filename = "";
|
this.filename = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
this.filename = ps[0].filename;
|
this.filename = (ps[0] || {}).filename;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
+2
-1
@@ -13,7 +13,8 @@
|
|||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"types": [
|
"types": [
|
||||||
"webpack-env",
|
"webpack-env",
|
||||||
"mocha"
|
"mocha",
|
||||||
|
"node"
|
||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": [
|
"@/*": [
|
||||||
|
|||||||
Reference in new issue
Block a user