Deploying to gh-pages from @ 6d5ba96281
🚀
233
assets/css/app.css
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input, button, textarea {
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
em {
|
||||||
|
font-style: normal !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
font-family: 'PingFang SC', BlinkMacSystemFont, Roboto, 'Helvetica Neue', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-message__icon {
|
||||||
|
display: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top {
|
||||||
|
height: 60px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.web-title {
|
||||||
|
margin: 0 15px 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.web-icon {
|
||||||
|
width: auto;
|
||||||
|
height: 1.5rem;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
#editor {
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
|
border: none;
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ctrl {
|
||||||
|
flex-basis: 60px;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-wrapper {
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
|
||||||
|
padding: 0;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
display: flex;
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-section {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint {
|
||||||
|
opacity: 0.6;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
margin: 0 -20px;
|
||||||
|
width: 375px;
|
||||||
|
padding: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 60px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview table {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-collapse: collapse;
|
||||||
|
display: table;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
.preview table tr:nth-child(even){
|
||||||
|
background: rgb(250, 250, 250);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
.select-item-left {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-item-right {
|
||||||
|
float: right;
|
||||||
|
color: #8492a6;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror {
|
||||||
|
height: 100% !important;
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 20px;
|
||||||
|
width: 100% !important;
|
||||||
|
font-family: 'PingFang SC', BlinkMacSystemFont, Roboto, 'Helvetica Neue', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ele ui */
|
||||||
|
.el-form-item {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tooltip {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*wechat code block*/
|
||||||
|
.rich_media_content .code-snippet *, .rich_media_content .code-snippet__fix * {
|
||||||
|
max-width: 1000% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-snippet__fix {
|
||||||
|
word-wrap: break-word !important;
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 10px 8px;
|
||||||
|
color: #333;
|
||||||
|
position: relative;
|
||||||
|
background-color: rgba(0, 0, 0, 0.03);
|
||||||
|
border: 1px solid #f0f0f0;
|
||||||
|
border-radius: 2px;
|
||||||
|
display: flex;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-snippet__fix .code-snippet__line-index {
|
||||||
|
counter-reset: line;
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: 100%;
|
||||||
|
padding: 1em;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-snippet__fix .code-snippet__line-index li {
|
||||||
|
list-style-type: none;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-snippet__fix .code-snippet__line-index li::before {
|
||||||
|
min-width: 1.5em;
|
||||||
|
text-align: right;
|
||||||
|
left: -2.5em;
|
||||||
|
counter-increment: line;
|
||||||
|
content: counter(line);
|
||||||
|
display: inline;
|
||||||
|
color: rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-snippet__fix pre {
|
||||||
|
overflow-x: auto;
|
||||||
|
padding: 1em 1em 1em 1em;
|
||||||
|
white-space: normal;
|
||||||
|
flex: 1;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-snippet__fix code {
|
||||||
|
text-align: left;
|
||||||
|
font-size: 14px;
|
||||||
|
white-space: pre;
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
background-color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: rgba(200, 200, 200, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: rgba(144, 146, 152, 0.5);
|
||||||
|
transition: background-color .3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: rgba(144, 146, 152, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-vscrollbar:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-scroll, .preview-wrapper {
|
||||||
|
overflow: unset!important;
|
||||||
|
overflow-y: scroll!important;
|
||||||
|
}
|
43
assets/css/loading.css
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
.loading {
|
||||||
|
text-align: center;
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 99999;
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
-webkit-transform: translateX(-50%) translateY(-50%);
|
||||||
|
-moz-transform: translateX(-50%) translateY(-50%);
|
||||||
|
-ms-transform: translateX(-50%) translateY(-50%);
|
||||||
|
transform: translateX(-50%) translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-text {
|
||||||
|
line-height: 1.4;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-anim {
|
||||||
|
width: 35px;
|
||||||
|
height: 35px;
|
||||||
|
border: 5px solid rgba(189, 189, 189, 0.25);
|
||||||
|
border-left-color: rgba(66, 185, 131, 0.9);
|
||||||
|
border-top-color: rgba(66, 185, 131, 0.9);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: inline-block;
|
||||||
|
animation: rotate 600ms infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotate {
|
||||||
|
to {
|
||||||
|
transform: rotate(1turn)
|
||||||
|
}
|
||||||
|
}
|
104
assets/css/style-mirror.css
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Name: Base16 Default Light
|
||||||
|
Author: Chris Kempson (http://chriskempson.com)
|
||||||
|
|
||||||
|
CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror)
|
||||||
|
Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16)
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
.cm-s-style-mirror.CodeMirror {
|
||||||
|
background: #f5f5f5;
|
||||||
|
color: #444;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 20px;
|
||||||
|
line-height: 25px;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror div.CodeMirror-selected {
|
||||||
|
background: #e0e0e0;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror .CodeMirror-line::selection,
|
||||||
|
.cm-s-style-mirror .CodeMirror-line > span::selection,
|
||||||
|
.cm-s-style-mirror .CodeMirror-line > span > span::selection {
|
||||||
|
background: #e0e0e0;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror .CodeMirror-line::-moz-selection,
|
||||||
|
.cm-s-style-mirror .CodeMirror-line > span::-moz-selection,
|
||||||
|
.cm-s-style-mirror .CodeMirror-line > span > span::-moz-selection {
|
||||||
|
background: #e0e0e0;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror .CodeMirror-gutters {
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-right: 0px;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror .CodeMirror-guttermarker {
|
||||||
|
color: #ac4142;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror .CodeMirror-guttermarker-subtle {
|
||||||
|
color: #b0b0b0;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror .CodeMirror-linenumber {
|
||||||
|
color: #b0b0b0;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror .CodeMirror-cursor {
|
||||||
|
border-left: 1px solid #505050;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-style-mirror span.cm-comment {
|
||||||
|
color:green;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror span.cm-atom {
|
||||||
|
color: #aa759f;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror span.cm-number {
|
||||||
|
color: #aa759f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-style-mirror span.cm-property,
|
||||||
|
.cm-s-style-mirror span.cm-attribute {
|
||||||
|
color: #90a959;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror span.cm-keyword {
|
||||||
|
color: #023a52;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror span.cm-string {
|
||||||
|
color: #e46918;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-style-mirror span.cm-variable {
|
||||||
|
color: #90a959;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror span.cm-variable-2 {
|
||||||
|
color: #00695f;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror span.cm-variable-3 {
|
||||||
|
color: #2e6e8a;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror span.cm-def {
|
||||||
|
color: #d28445;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror span.cm-bracket {
|
||||||
|
color: #202020;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror span.cm-tag {
|
||||||
|
color:#000;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror span.cm-link {
|
||||||
|
color: #b26a00;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror span.cm-error {
|
||||||
|
/* background: #ac4142;
|
||||||
|
color: #f5f5f5; */
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-style: wavy;
|
||||||
|
text-decoration-color: #df8d8e;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror .CodeMirror-activeline-background {
|
||||||
|
background: #dddcdc;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror .CodeMirror-matchingbracket {
|
||||||
|
color: rgb(32,32,32) !important;
|
||||||
|
background-color: rgba(0,0,0,0.1) !important;
|
||||||
|
}
|
||||||
|
|
BIN
assets/images/dfa.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
assets/images/doocs-md-copy-and-paste.gif
Normal file
After Width: | Height: | Size: 2.5 MiB |
BIN
assets/images/doocs-md-custom-css.gif
Normal file
After Width: | Height: | Size: 852 KiB |
BIN
assets/images/doocs-md-select-and-change-color-theme.gif
Normal file
After Width: | Height: | Size: 1.6 MiB |
BIN
assets/images/doocs-md-upload-image.gif
Normal file
After Width: | Height: | Size: 2.7 MiB |
BIN
assets/images/favicon.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/images/feedback.gif
Normal file
After Width: | Height: | Size: 190 KiB |
BIN
assets/images/juej.gif
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
assets/images/juex.gif
Normal file
After Width: | Height: | Size: 57 KiB |
BIN
assets/images/logo-2.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
assets/images/logo.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
assets/images/prefixtree.png
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
assets/images/qrcode-for-doocs.gif
Normal file
After Width: | Height: | Size: 1.4 MiB |
BIN
assets/images/qrcode-for-doocs.jpg
Normal file
After Width: | Height: | Size: 65 KiB |
BIN
assets/images/qrcode-for-yanglbme.jpg
Normal file
After Width: | Height: | Size: 70 KiB |
99
assets/scripts/default-content.js
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
const DEFAULT_CONTENT =
|
||||||
|
`# 示例文章:Google 搜索的即时自动补全功能究竟是如何“工作”的?
|
||||||
|
> Google 搜索**自动补全功能**的强大,相信不少朋友都能感受到,它帮助我们更快地“补全”我们所要输入的搜索关键字。那么,它怎么知道我们要输入什么内容?它又是如何工作的?在这篇文章里,我们一起来看看。
|
||||||
|
|
||||||
|
## 使用自动补全
|
||||||
|
Google 搜索的自动补全功能可以在 Google 搜索应用的大多数位置使用,包括 [Google](https://www.google.com/) 主页、适用于 IOS 和 Android 的 Google 应用,我们只需要在 Google 搜索框上开始键入关键字,就可以看到联想词了。
|
||||||
|
|
||||||
|
![](https://imgkr.cn-bj.ufileos.com/17ed83bf-e028-4db2-9503-5a3b4e64deee.gif)
|
||||||
|
|
||||||
|
在上图示例中,我们可以看到,输入关键字 \`juej\`,Google 搜索会联想到“掘金”、“掘金小册”、“绝句”等等,好处就是,我们无须输入完整的关键字即可轻松完成针对这些 topics 的搜索。
|
||||||
|
|
||||||
|
谷歌搜索的自动补全功能对于使用移动设备的用户来说特别有用,用户可以轻松在难以键入的小屏幕上完成搜索。当然,对于移动设备用户和台式机用户而言,这都节省了大量的时间。根据 Google 官方报告,自动补全功能可以减少大约 25% 的打字,累积起来,预计每天可以节省 200 多年的打字时间。是的,每天!
|
||||||
|
|
||||||
|
> 注意,本文所提到的“**联想词**”与“**预测**”,是同一个意思。
|
||||||
|
|
||||||
|
## 基于“预测”而非“建议”
|
||||||
|
Google 官方将自动补全功能称之为“预测”,而不是“建议”,为什么呢?其实是有充分理由的。自动补全功能是为了**帮助用户完成他们打算进行的搜索**,而不是建议用户要执行什么搜索。
|
||||||
|
|
||||||
|
那么,Google 是如何确定这些“预测”的?其实,Google 会根据趋势搜索 [trends](https://trends.google.com/trends/?geo=US) 给到我们这些“预测”。简单来说,哪个热门、哪个搜索频率高,就更可能推给我们。当然,这也与我们当前所处的位置以及我们的搜索历史相关。
|
||||||
|
|
||||||
|
另外,这些“预测”也会随着我们键入的关键字的变更而更改。例如,当我们把键入的关键字从 \`juej\` 更改为 \`juex\` 时,与“掘金”相关的预测会“消失”,同时,与“觉醒”、“决心”相关联的词会出现。
|
||||||
|
|
||||||
|
![](https://imgkr.cn-bj.ufileos.com/5b17dc99-606d-42c1-9f86-e09e88aaa822.gif)
|
||||||
|
|
||||||
|
## 为什么看不到某些联想词?
|
||||||
|
如果我们在输入某个关键字时看不到联想词,那么表明 Google 的算法可能检测到:
|
||||||
|
|
||||||
|
- 这个关键字不是热门字词;
|
||||||
|
- 搜索的字词太新了,我们可能需要等待几天或几周才能看到联想词;
|
||||||
|
- 这是一个侮辱性或敏感字词,这个搜索字词违反了 Google 的相关政策。更加详细的情况,可以了解 [Google 搜索自动补全政策](https://support.google.com/websearch/answer/7368877)。
|
||||||
|
|
||||||
|
## 为什么会看到某些不当的联想词?
|
||||||
|
Google 拥有专门设计的系统,可以自动捕获不适当的预测结果而不显示出来。然而,Google 每天需要处理数十亿次搜索,这意味着 Google 每天会显示数十亿甚至上百亿条预测。再好的系统,也可能存在缺陷,不正确的预测也可能随时会出现。
|
||||||
|
|
||||||
|
我们作为 Google 搜索的用户,如果认定某条预测违反了相关的搜索自动补全政策,可以进行举报反馈,点击右下角“**举报不当的联想查询**”并勾选相关选项即可。
|
||||||
|
|
||||||
|
![](https://imgkr.cn-bj.ufileos.com/6ca8185d-12c6-4550-bb4e-e49cfbf56db7.gif)
|
||||||
|
|
||||||
|
## 如何实现自动补全算法?
|
||||||
|
目前,Google 官方似乎并没有公开搜索自动补全的算法实现,但是业界在这方面已经有了不少研究。
|
||||||
|
|
||||||
|
一个好的自动补全器必须是快速的,并且在用户键入下一个字符后立即更新联想词列表。**自动补全器的核心是一个函数,它接受输入的前缀,并搜索以给定前缀开头的词汇或语句列表**。通常来说,只需要返回少量的数目即可。
|
||||||
|
|
||||||
|
接下来,我们先从一个简单且低效的实现开始,并在此基础上逐步构建更高效的方法。
|
||||||
|
|
||||||
|
### 词汇表实现
|
||||||
|
一个**简单粗暴的实现方式**是:顺序查找词汇表,依次检查每个词汇,看它是否以给定的前缀开头。
|
||||||
|
|
||||||
|
但是,此方法需要将前缀与每个词汇进行匹配检查,若词汇量较少,这种方式可能勉强行得通。但是,如果词汇量规模较大,效率就太低了。
|
||||||
|
|
||||||
|
一个**更好的实现方式是**:让词汇按字典顺序排序。借助二分搜索算法,可以快速搜索有序词汇表中的前缀。由于二分搜索的每一步都会将搜索的范围减半,因此,总的搜索时间与词汇表中单词数量的对数成正比,即时间复杂度是 \`O(log N)\`。二分搜索的性能很好,但有没有更好的实现呢?当然有,往下看。
|
||||||
|
|
||||||
|
### 前缀树实现
|
||||||
|
通常来说,许多词汇都以相同的前缀开头,比如 \`need\`、\`nested\` 都以 \`ne\` 开头,\`seed\`、\`speed\` 都以 \`s\` 开头。要是为每个单词分别存储公共前缀似乎很浪费。
|
||||||
|
|
||||||
|
![](https://imgkr.cn-bj.ufileos.com/7cc3cf37-040a-420e-8ef9-d05e92c82cfd.png)
|
||||||
|
|
||||||
|
前缀树是一种利用公共前缀来加速补全速度的数据结构。前缀树在节点树中排列一组单词,单词沿着从根节点到叶子节点的路径存储,树的层次对应于前缀的字母位置。
|
||||||
|
|
||||||
|
前缀的补全是顺着前缀定义的路径来查找的。例如,在上图的前缀树中,前缀 \`ne\` 对应于从子节点取左边缘 \`N\` 和唯一边缘 \`E\` 的路径。然后可以通过继续遍历从 \`E\` 节点可以达到的所有叶节点来生成补全列表。在图中,\`ne\` 的补全可以是两个分支:\`-ed\` 和 \`-sted\`。如果在数中找不到由前缀定义的路径,则说明词汇表中不包含以该前缀开头的单词。
|
||||||
|
|
||||||
|
### 有限状态自动机(DFA)实现
|
||||||
|
前缀树可以有效处理公共前缀,但是,对于其他共享词部分,仍会分别存储在每个分支中。比如,后缀 \`ed\`、\`ing\`、\`tion\` 在英文单词中特别常见。在上一个例子中,\`e\`、\`d\` 分别存放在了每一个分支上。
|
||||||
|
|
||||||
|
有没有一种方法可以更加节省存储空间呢?有的,那就是 DFA。
|
||||||
|
|
||||||
|
<center>
|
||||||
|
<img src="https://imgkr.cn-bj.ufileos.com/02bc143e-e1a7-4b3c-bd5d-8d6d39139f0a.png" style="width: 50%;"></center>
|
||||||
|
|
||||||
|
在上面的例子中,单词 \`need\`、\`nested\`、\`seed\` 和 \`speed\` 仅由 9 个节点组成,而上一张图中的前缀树包含了 17 个节点。
|
||||||
|
|
||||||
|
可以看出,最小化前缀树 DFA 可以在很大程度上减少数据结构的大小。即使词汇量很大,最小化 DFA 通常也适合在内存中存储,避免昂贵的磁盘访问是实现快速自动补全的关键。
|
||||||
|
|
||||||
|
### 一些扩展
|
||||||
|
上面介绍了如何利用合理的数据结构实现基本的自动补全功能。这些数据结构可以通过多种方式进行扩展,从而改善用户体验。
|
||||||
|
|
||||||
|
通常,满足特定前缀的词汇可能很多,而用户界面上能够显示的却不多,我们更希望能显示最常搜索或者最有价值的词汇。这通常可以通过为词汇表中的每个单词增加一个代表单词值的**权重** \`weight\`,并且按照权重高低来排序自动补全列表。
|
||||||
|
|
||||||
|
- 对于排序后的词汇表来说,在词汇表每个元素上增加 \`weight\` 属性并不难;
|
||||||
|
- 对于前缀树来说,将 \`weight\` 存储在叶子节点中,也是很简单的一个实现;
|
||||||
|
- 对于 \`DFA\` 来说,则较为复杂。因为一个叶子节点可以通过多条路径到达。一种解决方案是将权重关联到路径而不是叶子节点。
|
||||||
|
|
||||||
|
目前有不少开源库都提供了这个功能,比如主流的搜索引擎框架 [Elasticsearch](https://www.elastic.co/products/elasticsearch)、[Solr](https://lucene.apache.org/solr/) 等,基于此,我们可以实现高效而强大的自动补全功能。
|
||||||
|
|
||||||
|
#### 推荐阅读
|
||||||
|
- [阿里又一个 20k+ stars 开源项目诞生,恭喜 fastjson!](https://mp.weixin.qq.com/s/RNKDCK2KoyeuMeEs6GUrow)
|
||||||
|
- [刷掉 90% 候选人的互联网大厂海量数据面试题(附题解 + 方法总结)](https://mp.weixin.qq.com/s/rjGqxUvrEqJNlo09GrT1Dw)
|
||||||
|
- [好用!期待已久的文本块功能究竟如何在 Java 13 中发挥作用?](https://mp.weixin.qq.com/s/kalGv5T8AZGxTnLHr2wDsA)
|
||||||
|
- [2019 GitHub 开源贡献排行榜新鲜出炉!微软谷歌领头,阿里跻身前 12!](https://mp.weixin.qq.com/s/_q812aGD1b9QvZ2WFI0Qgw)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
欢迎关注我的公众号“**Doocs开源社区**”,原创技术文章第一时间推送。
|
||||||
|
|
||||||
|
<center>
|
||||||
|
<img src="https://imgkr.cn-bj.ufileos.com/1092dc45-e817-4bb0-82b0-2b2b4826ccf2.gif" style="width: 100px;">
|
||||||
|
</center>
|
||||||
|
|
||||||
|
`
|
190
assets/scripts/renderers/wx-renderer.js
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
let WxRenderer = function (opts) {
|
||||||
|
this.opts = opts;
|
||||||
|
let ENV_STRETCH_IMAGE = true;
|
||||||
|
|
||||||
|
let footnotes = [];
|
||||||
|
let footnoteIndex = 0;
|
||||||
|
let styleMapping = null;
|
||||||
|
|
||||||
|
const CODE_FONT_FAMILY = "Menlo, Operator Mono, Consolas, Monaco, monospace";
|
||||||
|
|
||||||
|
let merge = (base, extend) => Object.assign({}, base, extend);
|
||||||
|
|
||||||
|
this.buildTheme = themeTpl => {
|
||||||
|
let mapping = {};
|
||||||
|
let base = merge(themeTpl.BASE, {
|
||||||
|
'font-family': this.opts.fonts,
|
||||||
|
'font-size': this.opts.size
|
||||||
|
});
|
||||||
|
let base_block = merge(base, {});
|
||||||
|
for (let ele in themeTpl.inline) {
|
||||||
|
if (themeTpl.inline.hasOwnProperty(ele)) {
|
||||||
|
let style = themeTpl.inline[ele];
|
||||||
|
if (ele === 'codespan') {
|
||||||
|
style['font-family'] = CODE_FONT_FAMILY;
|
||||||
|
style['white-space'] = 'normal';
|
||||||
|
}
|
||||||
|
mapping[ele] = merge(base, style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let ele in themeTpl.block) {
|
||||||
|
if (themeTpl.block.hasOwnProperty(ele)) {
|
||||||
|
let style = themeTpl.block[ele];
|
||||||
|
if (ele === 'code') {
|
||||||
|
style['font-family'] = CODE_FONT_FAMILY;
|
||||||
|
}
|
||||||
|
mapping[ele] = merge(base_block, style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mapping;
|
||||||
|
};
|
||||||
|
|
||||||
|
let getStyles = (tokenName, addition) => {
|
||||||
|
let arr = [];
|
||||||
|
let dict = styleMapping[tokenName];
|
||||||
|
if (!dict) return '';
|
||||||
|
for (const key in dict) {
|
||||||
|
arr.push(key + ':' + dict[key]);
|
||||||
|
}
|
||||||
|
return `style="${arr.join(';') + (addition || '')}"`;
|
||||||
|
};
|
||||||
|
|
||||||
|
let addFootnote = (title, link) => {
|
||||||
|
footnotes.push([++footnoteIndex, title, link]);
|
||||||
|
return footnoteIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.buildFootnotes = () => {
|
||||||
|
let footnoteArray = footnotes.map(x => {
|
||||||
|
if (x[1] === x[2]) {
|
||||||
|
return `<code style="font-size: 90%; opacity: 0.6;">[${x[0]}]</code>: <i>${x[1]}</i><br/>`;
|
||||||
|
}
|
||||||
|
return `<code style="font-size: 90%; opacity: 0.6;">[${x[0]}]</code> ${x[1]}: <i>${x[2]}</i><br/>`;
|
||||||
|
});
|
||||||
|
return `<h4 ${getStyles('h4')}>引用链接</h4><p ${getStyles('footnotes')}>${footnoteArray.join('\n')}</p>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.buildAddition = () => {
|
||||||
|
return `
|
||||||
|
<style>
|
||||||
|
.preview-wrapper pre::before {
|
||||||
|
font-family: "SourceSansPro", "HelveticaNeue", Arial, sans-serif;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
color: #ccc;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.8em;
|
||||||
|
padding: 5px 10px 0;
|
||||||
|
line-height: 15px;
|
||||||
|
height: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setOptions = newOpts => {
|
||||||
|
this.opts = merge(this.opts, newOpts);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.hasFootnotes = () => footnotes.length !== 0;
|
||||||
|
|
||||||
|
this.getRenderer = (status) => {
|
||||||
|
footnotes = [];
|
||||||
|
footnoteIndex = 0;
|
||||||
|
|
||||||
|
styleMapping = this.buildTheme(this.opts.theme);
|
||||||
|
let renderer = new marked.Renderer();
|
||||||
|
|
||||||
|
renderer.heading = (text, level) => {
|
||||||
|
switch (level) {
|
||||||
|
case 1:
|
||||||
|
return `<h1 ${getStyles('h1')}>${text}</h1>`;
|
||||||
|
case 2:
|
||||||
|
return `<h2 ${getStyles('h2')}>${text}</h2>`;
|
||||||
|
case 3:
|
||||||
|
return `<h3 ${getStyles('h3')}>${text}</h3>`;
|
||||||
|
default:
|
||||||
|
return `<h4 ${getStyles('h4')}>${text}</h4>`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
renderer.paragraph = text => `<p ${getStyles('p')}>${text}</p>`;
|
||||||
|
|
||||||
|
renderer.blockquote = text => {
|
||||||
|
text = text.replace(/<p.*?>/, `<p ${getStyles('blockquote_p')}>`);
|
||||||
|
return `<blockquote ${getStyles('blockquote')}>${text}</blockquote>`;
|
||||||
|
};
|
||||||
|
renderer.code = (text, infoString) => {
|
||||||
|
text = text.replace(/</g, "<");
|
||||||
|
text = text.replace(/>/g, ">");
|
||||||
|
|
||||||
|
let lines = text.split('\n');
|
||||||
|
let codeLines = [];
|
||||||
|
let numbers = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const line = lines[i];
|
||||||
|
codeLines.push(`<code class="prettyprint"><span class="code-snippet_outer">${(line || '<br>')}</span></code>`);
|
||||||
|
numbers.push('<li></li>');
|
||||||
|
}
|
||||||
|
let lang = infoString || '';
|
||||||
|
|
||||||
|
return `
|
||||||
|
<section class="code-snippet__fix code-snippet__js">
|
||||||
|
<ul class="code-snippet__line-index code-snippet__js">${numbers.join('')}</ul>
|
||||||
|
<pre class="code-snippet__js" data-lang="${lang}">
|
||||||
|
${codeLines.join('')}
|
||||||
|
</pre>
|
||||||
|
</section>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
renderer.codespan = (text, infoString) => `<code ${getStyles('codespan')}>${text}</code>`;
|
||||||
|
renderer.listitem = text => `<span ${getStyles('listitem')}><span style="margin-right: 10px;"><%s/></span>${text}</span>`;
|
||||||
|
|
||||||
|
renderer.list = (text, ordered, start) => {
|
||||||
|
text = text.replace(/<\/*p.*?>/g, '');
|
||||||
|
let segments = text.split(`<%s/>`);
|
||||||
|
if (!ordered) {
|
||||||
|
text = segments.join('•');
|
||||||
|
return `<p ${getStyles('ul')}>${text}</p>`;
|
||||||
|
}
|
||||||
|
text = segments[0];
|
||||||
|
for (let i = 1; i < segments.length; i++) {
|
||||||
|
text = text + i + '.' + segments[i];
|
||||||
|
}
|
||||||
|
return `<p ${getStyles('ol')}>${text}</p>`;
|
||||||
|
};
|
||||||
|
renderer.image = (href, title, text) => {
|
||||||
|
let subText = '';
|
||||||
|
if (text) {
|
||||||
|
subText = `<figcaption ${getStyles('figcaption')}>${text}</figcaption>`;
|
||||||
|
}
|
||||||
|
let figureStyles = getStyles('figure');
|
||||||
|
let imgStyles = getStyles(ENV_STRETCH_IMAGE ? 'image' : 'image_org');
|
||||||
|
return `<figure ${figureStyles}><img ${imgStyles} src="${href}" title="${title}" alt="${text}"/>${subText}</figure>`;
|
||||||
|
};
|
||||||
|
renderer.link = (href, title, text) => {
|
||||||
|
if (href.indexOf('https://mp.weixin.qq.com') === 0) {
|
||||||
|
return `<a href="${href}" title="${(title || text)}" ${getStyles('wx_link')}>${text}</a>`;
|
||||||
|
} else if (href === text) {
|
||||||
|
return text;
|
||||||
|
} else {
|
||||||
|
if (status) {
|
||||||
|
let ref = addFootnote(title || text, href);
|
||||||
|
return `<span ${getStyles('link')}>${text}<sup>[${ref}]</sup></span>`;
|
||||||
|
} else {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
renderer.strong = text => `<strong ${getStyles('strong')}>${text}</strong>`;
|
||||||
|
renderer.em = text => `<p ${getStyles('p', ';font-style: italic;')}>${text}</p>`;
|
||||||
|
renderer.table = (header, body) => `<section style="padding:0 8px;"><table class="preview-table"><thead ${getStyles('thead')}>${header}</thead><tbody>${body}</tbody></table></section>`;
|
||||||
|
// renderer.tablerow = (text) => `<tr style="">${text}</tr>`;
|
||||||
|
renderer.tablecell = (text, flags) => `<td ${getStyles('td')}>${text}</td>`;
|
||||||
|
renderer.hr = () => `<hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);">`;
|
||||||
|
return renderer;
|
||||||
|
}
|
||||||
|
};
|
41
assets/scripts/themes/default-theme-css.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
const DEFAULT_CSS_CONTENT =
|
||||||
|
`/*
|
||||||
|
按Ctrl+F可格式化
|
||||||
|
*/
|
||||||
|
/* 一级标题样式 */
|
||||||
|
h1 {
|
||||||
|
}
|
||||||
|
/* 二级标题样式 */
|
||||||
|
h2 {
|
||||||
|
}
|
||||||
|
/* 三级标题样式 */
|
||||||
|
h3 {
|
||||||
|
}
|
||||||
|
/* 四级标题样式 */
|
||||||
|
h4 {
|
||||||
|
}
|
||||||
|
/* 图片样式 */
|
||||||
|
image {
|
||||||
|
}
|
||||||
|
/* 引用样式 */
|
||||||
|
blockquote {
|
||||||
|
}
|
||||||
|
/* 引用段落样式 */
|
||||||
|
blockquote_p {
|
||||||
|
}
|
||||||
|
/* 段落样式 */
|
||||||
|
p {
|
||||||
|
}
|
||||||
|
/* 行内代码样式 */
|
||||||
|
codespan {
|
||||||
|
}
|
||||||
|
/* 粗体样式 */
|
||||||
|
strong {
|
||||||
|
}
|
||||||
|
/* 链接样式 */
|
||||||
|
link {
|
||||||
|
}
|
||||||
|
/* 微信链接样式 */
|
||||||
|
wx_link {
|
||||||
|
}
|
||||||
|
`
|
177
assets/scripts/themes/default-theme.js
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
export const default_theme = {
|
||||||
|
BASE: {
|
||||||
|
'text-align': 'left',
|
||||||
|
'color': '#3f3f3f',
|
||||||
|
'line-height': '1.75'
|
||||||
|
},
|
||||||
|
BASE_BLOCK: {
|
||||||
|
'margin': '1em 8px'
|
||||||
|
},
|
||||||
|
block: {
|
||||||
|
// 一级标题样式
|
||||||
|
h1: {
|
||||||
|
'font-size': '1.2em',
|
||||||
|
'text-align': 'center',
|
||||||
|
'font-weight': 'bold',
|
||||||
|
'display': 'table',
|
||||||
|
'margin': '2em auto 1em',
|
||||||
|
'padding': '0 1em',
|
||||||
|
'border-bottom': '2px solid rgba(0, 152, 116, 0.9)'
|
||||||
|
},
|
||||||
|
|
||||||
|
// 二级标题样式
|
||||||
|
h2: {
|
||||||
|
'font-size': '1.2em',
|
||||||
|
'text-align': 'center',
|
||||||
|
'font-weight': 'bold',
|
||||||
|
'display': 'table',
|
||||||
|
'margin': '4em auto 2em',
|
||||||
|
'padding': '0 0.2em',
|
||||||
|
'background': 'rgba(0, 152, 116, 0.9)',
|
||||||
|
'color': '#fff'
|
||||||
|
},
|
||||||
|
|
||||||
|
// 三级标题样式
|
||||||
|
h3: {
|
||||||
|
'font-weight': 'bold',
|
||||||
|
'font-size': '1.1em',
|
||||||
|
'margin': '2em 8px 0.75em 0',
|
||||||
|
'line-height': '1.2',
|
||||||
|
'padding-left': '8px',
|
||||||
|
'border-left': '3px solid rgba(0, 152, 116, 0.9)'
|
||||||
|
},
|
||||||
|
|
||||||
|
// 四级标题样式
|
||||||
|
h4: {
|
||||||
|
'font-weight': 'bold',
|
||||||
|
'font-size': '1em',
|
||||||
|
'margin': '2em 8px 0.5em',
|
||||||
|
'color': 'rgba(66, 185, 131, 0.9)'
|
||||||
|
},
|
||||||
|
|
||||||
|
// 段落样式
|
||||||
|
p: {
|
||||||
|
'margin': '1.5em 8px',
|
||||||
|
'letter-spacing': '0.1em'
|
||||||
|
},
|
||||||
|
|
||||||
|
// 引用样式
|
||||||
|
blockquote: {
|
||||||
|
'font-style': 'normal',
|
||||||
|
'border-left': 'none',
|
||||||
|
'padding': '1em',
|
||||||
|
'border-radius': '4px',
|
||||||
|
'color': '#FEEEED',
|
||||||
|
'background': 'rgba(27,31,35,.05)',
|
||||||
|
'margin': '2em 8px'
|
||||||
|
},
|
||||||
|
|
||||||
|
blockquote_p: {
|
||||||
|
'letter-spacing': '0.1em',
|
||||||
|
'color': 'rgb(80, 80, 80)',
|
||||||
|
'font-family': 'PingFangSC-light, PingFangTC-light, Open Sans, Helvetica Neue, sans-serif',
|
||||||
|
'font-size': '1em',
|
||||||
|
'display': 'inline'
|
||||||
|
},
|
||||||
|
|
||||||
|
code: {
|
||||||
|
'font-size': '80%',
|
||||||
|
'overflow': 'auto',
|
||||||
|
'color': '#333',
|
||||||
|
'background': 'rgb(247, 247, 247)',
|
||||||
|
'border-radius': '2px',
|
||||||
|
'padding': '10px',
|
||||||
|
'line-height': '1.5',
|
||||||
|
'border': '1px solid rgb(236,236,236)',
|
||||||
|
'margin': '20px 0'
|
||||||
|
},
|
||||||
|
|
||||||
|
image: {
|
||||||
|
'border-radius': '4px',
|
||||||
|
'display': 'block',
|
||||||
|
'margin': '0.1em auto 0.5em',
|
||||||
|
'width': '100% !important'
|
||||||
|
},
|
||||||
|
|
||||||
|
image_org: {
|
||||||
|
'border-radius': '4px',
|
||||||
|
'display': 'block'
|
||||||
|
},
|
||||||
|
|
||||||
|
ol: {
|
||||||
|
'margin-left': '0',
|
||||||
|
'padding-left': '1em'
|
||||||
|
},
|
||||||
|
|
||||||
|
ul: {
|
||||||
|
'margin-left': '0',
|
||||||
|
'padding-left': '1em',
|
||||||
|
'list-style': 'circle'
|
||||||
|
},
|
||||||
|
|
||||||
|
footnotes: {
|
||||||
|
'margin': '0.5em 8px',
|
||||||
|
'font-size': '80%'
|
||||||
|
},
|
||||||
|
|
||||||
|
figure: {
|
||||||
|
'margin': '1.5em 8px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inline: {
|
||||||
|
listitem: {
|
||||||
|
'text-indent': '-1em',
|
||||||
|
'display': 'block',
|
||||||
|
'margin': '0.2em 8px'
|
||||||
|
},
|
||||||
|
|
||||||
|
codespan: {
|
||||||
|
'font-size': '90%',
|
||||||
|
'color': '#d14',
|
||||||
|
'background': 'rgba(27,31,35,.05)',
|
||||||
|
'padding': '3px 5px',
|
||||||
|
'border-radius': '4px'
|
||||||
|
},
|
||||||
|
|
||||||
|
link: {
|
||||||
|
'color': '#576b95'
|
||||||
|
},
|
||||||
|
|
||||||
|
wx_link: {
|
||||||
|
'color': '#576b95',
|
||||||
|
'text-decoration': 'none'
|
||||||
|
},
|
||||||
|
|
||||||
|
// 字体加粗样式
|
||||||
|
strong: {
|
||||||
|
'color': 'rgba(15, 76, 129, 0.9)',
|
||||||
|
'font-weight': 'bold'
|
||||||
|
},
|
||||||
|
|
||||||
|
table: {
|
||||||
|
'border-collapse': 'collapse',
|
||||||
|
'text-align': 'center',
|
||||||
|
'margin': '1em 8px'
|
||||||
|
},
|
||||||
|
|
||||||
|
thead: {
|
||||||
|
'background': 'rgba(0, 0, 0, 0.05)',
|
||||||
|
'font-weight': 'bold'
|
||||||
|
},
|
||||||
|
|
||||||
|
td: {
|
||||||
|
'border': '1px solid #dfdfdf',
|
||||||
|
'padding': '0.25em 0.5em'
|
||||||
|
},
|
||||||
|
|
||||||
|
footnote: {
|
||||||
|
'font-size': '12px'
|
||||||
|
},
|
||||||
|
|
||||||
|
figcaption: {
|
||||||
|
'text-align': 'center',
|
||||||
|
'color': '#888',
|
||||||
|
'font-size': '0.8em'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
143
assets/scripts/util.js
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
// 设置自定义颜色
|
||||||
|
function setColorWithTemplate(template) {
|
||||||
|
return function (color) {
|
||||||
|
let custom_theme = JSON.parse(JSON.stringify(template));
|
||||||
|
custom_theme.block.h1['border-bottom'] = `2px solid ${color}`;
|
||||||
|
custom_theme.block.h2['background'] = color;
|
||||||
|
custom_theme.block.h3['border-left'] = `3px solid ${color}`;
|
||||||
|
custom_theme.block.h4['color'] = color;
|
||||||
|
custom_theme.inline.strong['color'] = color;
|
||||||
|
return custom_theme;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let setColorWithCustomTemplate = function setColorWithCustomTemplate(template, color) {
|
||||||
|
let custom_theme = JSON.parse(JSON.stringify(template));
|
||||||
|
custom_theme.block.h1['border-bottom'] = `2px solid ${color}`;
|
||||||
|
custom_theme.block.h2['background'] = color;
|
||||||
|
custom_theme.block.h3['border-left'] = `3px solid ${color}`;
|
||||||
|
custom_theme.block.h4['color'] = color;
|
||||||
|
custom_theme.inline.strong['color'] = color;
|
||||||
|
return custom_theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置自定义字体大小
|
||||||
|
function setFontSizeWithTemplate(template) {
|
||||||
|
return function (fontSize) {
|
||||||
|
let custom_theme = JSON.parse(JSON.stringify(template));
|
||||||
|
custom_theme.block.h1['font-size'] = `${fontSize * 1.14}px`;
|
||||||
|
custom_theme.block.h2['font-size'] = `${fontSize * 1.1}px`;
|
||||||
|
custom_theme.block.h3['font-size'] = `${fontSize}px`;
|
||||||
|
custom_theme.block.h4['font-size'] = `${fontSize}px`;
|
||||||
|
return custom_theme;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let setColor = setColorWithTemplate(default_theme);
|
||||||
|
let setFontSize = setFontSizeWithTemplate(default_theme);
|
||||||
|
|
||||||
|
function customCssWithTemplate(jsonString, color, theme) {
|
||||||
|
let custom_theme = JSON.parse(JSON.stringify(theme));
|
||||||
|
// block
|
||||||
|
custom_theme.block.h1['border-bottom'] = `2px solid ${color}`;
|
||||||
|
custom_theme.block.h2['background'] = color;
|
||||||
|
custom_theme.block.h3['border-left'] = `3px solid ${color}`;
|
||||||
|
custom_theme.block.h4['color'] = color;
|
||||||
|
custom_theme.inline.strong['color'] = color;
|
||||||
|
|
||||||
|
custom_theme.block.h1 = Object.assign(custom_theme.block.h1, jsonString.h1);
|
||||||
|
custom_theme.block.h2 = Object.assign(custom_theme.block.h2, jsonString.h2);
|
||||||
|
custom_theme.block.h3 = Object.assign(custom_theme.block.h3, jsonString.h3);
|
||||||
|
custom_theme.block.h4 = Object.assign(custom_theme.block.h4, jsonString.h4);
|
||||||
|
custom_theme.block.p = Object.assign(custom_theme.block.p, jsonString.p);
|
||||||
|
custom_theme.block.blockquote = Object.assign(custom_theme.block.blockquote, jsonString.blockquote);
|
||||||
|
custom_theme.block.blockquote_p = Object.assign(custom_theme.block.blockquote_p, jsonString.blockquote_p);
|
||||||
|
custom_theme.block.image = Object.assign(custom_theme.block.image, jsonString.image);
|
||||||
|
|
||||||
|
// inline
|
||||||
|
custom_theme.inline.strong = Object.assign(custom_theme.inline.strong, jsonString.strong);
|
||||||
|
custom_theme.inline.codespan = Object.assign(custom_theme.inline.codespan, jsonString.codespan);
|
||||||
|
custom_theme.inline.link = Object.assign(custom_theme.inline.link, jsonString.link);
|
||||||
|
custom_theme.inline.wx_link = Object.assign(custom_theme.inline.wx_link, jsonString.wx_link);
|
||||||
|
|
||||||
|
return custom_theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将CSS形式的字符串转换为JSON
|
||||||
|
*
|
||||||
|
* @param {css字符串} css
|
||||||
|
*/
|
||||||
|
function css2json(css) {
|
||||||
|
|
||||||
|
// 移除CSS所有注释
|
||||||
|
while ((open = css.indexOf("/*")) !== -1 &&
|
||||||
|
(close = css.indexOf("*/")) !== -1) {
|
||||||
|
css = css.substring(0, open) + css.substring(close + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化返回值
|
||||||
|
let json = {};
|
||||||
|
|
||||||
|
while (css.length > 0 && (css.indexOf('{') !== -1) && (css.indexOf('}') !== -1)) {
|
||||||
|
// 存储第一个左/右花括号的下标
|
||||||
|
const lbracket = css.indexOf('{');
|
||||||
|
const rbracket = css.indexOf('}');
|
||||||
|
|
||||||
|
// 第一步:将声明转换为Object,如:
|
||||||
|
// `font: 'Times New Roman' 1em; color: #ff0000; margin-top: 1em;`
|
||||||
|
// ==>
|
||||||
|
// `{"font": "'Times New Roman' 1em", "color": "#ff0000", "margin-top": "1em"}`
|
||||||
|
|
||||||
|
// 辅助方法:将array转为object
|
||||||
|
function toObject(array) {
|
||||||
|
let ret = {};
|
||||||
|
array.forEach(e => {
|
||||||
|
const index = e.indexOf(':');
|
||||||
|
const property = e.substring(0, index).trim();
|
||||||
|
const value = e.substring(index + 1).trim();
|
||||||
|
ret[property] = value;
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切割声明块并移除空白符,然后放入数组中
|
||||||
|
let declarations = css.substring(lbracket + 1, rbracket)
|
||||||
|
.split(";")
|
||||||
|
.map(e => e.trim())
|
||||||
|
.filter(e => e.length > 0); // 移除所有""空值
|
||||||
|
|
||||||
|
// 转为Object对象
|
||||||
|
declarations = toObject(declarations);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 第二步:选择器处理,每个选择器会与它对应的声明相关联,如:
|
||||||
|
// `h1, p#bar {color: red}`
|
||||||
|
// ==>
|
||||||
|
// {"h1": {color: red}, "p#bar": {color: red}}
|
||||||
|
|
||||||
|
let selectors = css.substring(0, lbracket)
|
||||||
|
// 以,切割,并移除空格:`"h1, p#bar, span.foo"` => ["h1", "p#bar", "span.foo"]
|
||||||
|
.split(",")
|
||||||
|
.map(selector => selector.trim());
|
||||||
|
|
||||||
|
// 迭代赋值
|
||||||
|
selectors.forEach(selector => {
|
||||||
|
// 若不存在,则先初始化
|
||||||
|
if (!json[selector]) json[selector] = {};
|
||||||
|
// 赋值到JSON
|
||||||
|
Object.keys(declarations).forEach(key => {
|
||||||
|
json[selector][key] = declarations[key];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 继续下个声明块
|
||||||
|
css = css.slice(rbracket + 1).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回JSON形式的结果串
|
||||||
|
return json;
|
||||||
|
}
|
1
css/app.64bfb241.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
.editor__header[data-v-257e3ffe]{width:100%}.header__item[data-v-257e3ffe]{margin:0 3px}.header__item_last[data-v-257e3ffe]{margin-right:8px}.header__switch[data-v-257e3ffe]{margin-left:8px}.main-body[data-v-a0bd6f2e]{padding-top:0}
|
1
css/chunk-vendors.3ea0baf3.css
Normal file
BIN
favicon.ico
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
fonts/element-icons.535877f5.woff
Normal file
BIN
fonts/element-icons.732389de.ttf
Normal file
1
index.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<!DOCTYPE html><html lang=en><head><meta charset=UTF-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=keywords content=md,markdown,markdown-editor,wechat,official-account,yanglbme,doocs><meta name=description content="Wechat Markdown Editor | 一款高度简洁的微信 Markdown 编辑器"><meta name=viewport content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1"><title>微信 Markdown 编辑器</title><link rel="shortcut icon" href=https://imgkr.cn-bj.ufileos.com/f3accc83-b854-4e99-afb5-8a6465e1d84f.png><link rel=apple-touch-icon-precomposed href=https://imgkr.cn-bj.ufileos.com/f3accc83-b854-4e99-afb5-8a6465e1d84f.png><link rel=stylesheet href=assets/css/loading.css><link rel=stylesheet href=libs/css/index.css><link rel=stylesheet href=libs/css/code-themes/github-v2.min.css><link rel=stylesheet href=libs/css/style-mirror.css><link rel=stylesheet href=libs/css/animate.css><link rel=stylesheet href=assets/css/app.css><link href=/md/js/about.b48c8998.js rel=prefetch><link href=/md/css/app.64bfb241.css rel=preload as=style><link href=/md/css/chunk-vendors.3ea0baf3.css rel=preload as=style><link href=/md/js/app.64660052.js rel=preload as=script><link href=/md/js/chunk-vendors.3f71fa3d.js rel=preload as=script><link href=/md/css/chunk-vendors.3ea0baf3.css rel=stylesheet><link href=/md/css/app.64bfb241.css rel=stylesheet></head><body><div id=app></div><script src=/md/js/chunk-vendors.3f71fa3d.js></script><script src=/md/js/app.64660052.js></script></body></html>
|
2
js/about.b48c8998.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["about"],{f820:function(t,e,n){"use strict";n.r(e);var a=function(){var t=this,e=t.$createElement;t._self._c;return t._m(0)},s=[function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"about"},[n("h1",[t._v("This is an about page")])])}],u=n("2877"),c={},i=Object(u["a"])(c,a,s,!1,null,null,null);e["default"]=i.exports}}]);
|
||||||
|
//# sourceMappingURL=about.b48c8998.js.map
|
1
js/about.b48c8998.js.map
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["webpack:///./src/views/About.vue?1bb4","webpack:///./src/views/About.vue"],"names":["render","_vm","this","_h","$createElement","_self","_c","_m","staticRenderFns","staticClass","_v","script","component"],"mappings":"8GAAA,IAAIA,EAAS,WAAa,IAAIC,EAAIC,KAASC,EAAGF,EAAIG,eAAsBH,EAAII,MAAMC,GAAO,OAAOL,EAAIM,GAAG,IACnGC,EAAkB,CAAC,WAAa,IAAIP,EAAIC,KAASC,EAAGF,EAAIG,eAAmBE,EAAGL,EAAII,MAAMC,IAAIH,EAAG,OAAOG,EAAG,MAAM,CAACG,YAAY,SAAS,CAACH,EAAG,KAAK,CAACL,EAAIS,GAAG,+B,YCAtJC,EAAS,GAKTC,EAAY,eACdD,EACAX,EACAQ,GACA,EACA,KACA,KACA,MAIa,aAAAI,E","file":"js/about.b48c8998.js","sourcesContent":["var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _vm._m(0)}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"about\"},[_c('h1',[_vm._v(\"This is an about page\")])])}]\n\nexport { render, staticRenderFns }","import { render, staticRenderFns } from \"./About.vue?vue&type=template&id=1ae8a7be&\"\nvar script = {}\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports"],"sourceRoot":""}
|
2
js/app.64660052.js
Normal file
1
js/app.64660052.js.map
Normal file
82
js/chunk-vendors.3f71fa3d.js
Normal file
1
js/chunk-vendors.3f71fa3d.js.map
Normal file
11
libs/css/animate.css
vendored
Normal file
2
libs/css/code-themes/github-v2.min.css
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/*! Color themes for Google Code Prettify | MIT License | github.com/jmblog/color-themes-for-google-code-prettify */
|
||||||
|
.prettyprint{font-family:Menlo,Bitstream Vera Sans Mono,DejaVu Sans Mono,Monaco,Consolas,monospace;border:0!important}.pln{color:#333}ol.linenums{margin-top:0;margin-bottom:0;color:#ccc}li.L0,li.L1,li.L2,li.L3,li.L4,li.L5,li.L6,li.L7,li.L8,li.L9{padding-left:1em;background-color:#fafbfc;list-style-type:decimal}@media screen{.str{color:#183691}.kwd{color:#a71d5d}.com{color:#969896}.typ{color:#0086b3}.lit{color:#0086b3}.pun{color:#333}.opn{color:#333}.clo{color:#333}.tag{color:navy}.atn{color:#795da3}.atv{color:#183691}.dec{color:#333}.var{color:teal}.fun{color:#900}}
|
2
libs/css/code-themes/tomorrow-night-eighties.min.css
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/*! Color themes for Google Code Prettify | MIT License | github.com/jmblog/color-themes-for-google-code-prettify */
|
||||||
|
.prettyprint{background:#2d2d2d!important;font-family:Menlo,Bitstream Vera Sans Mono,DejaVu Sans Mono,Monaco,Consolas,monospace;border:0!important}.pln{color:#ccc}ol.linenums{margin-top:0;margin-bottom:0;color:#999}li.L0,li.L1,li.L2,li.L3,li.L4,li.L5,li.L6,li.L7,li.L8,li.L9{padding-left:1em;background-color:#2d2d2d;list-style-type:decimal}@media screen{.str{color:#9c9}.kwd{color:#c9c}.com{color:#999}.typ{color:#69c}.lit{color:#f99157}.pun{color:#ccc}.opn{color:#ccc}.clo{color:#ccc}.tag{color:#f2777a}.atn{color:#f99157}.atv{color:#6cc}.dec{color:#f99157}.var{color:#f2777a}.fun{color:#69c}}
|
2
libs/css/code-themes/tomorrow-night.min.css
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/*! Color themes for Google Code Prettify | MIT License | github.com/jmblog/color-themes-for-google-code-prettify */
|
||||||
|
.prettyprint{background:#1d1f21!important;font-family:Menlo,Bitstream Vera Sans Mono,DejaVu Sans Mono,Monaco,Consolas,monospace;border:0!important}.pln{color:#c5c8c6}ol.linenums{margin-top:0;margin-bottom:0;color:#969896}li.L0,li.L1,li.L2,li.L3,li.L4,li.L5,li.L6,li.L7,li.L8,li.L9{padding-left:1em;background-color:#1d1f21;list-style-type:decimal}@media screen{.str{color:#b5bd68}.kwd{color:#b294bb}.com{color:#969896}.typ{color:#81a2be}.lit{color:#de935f}.pun{color:#c5c8c6}.opn{color:#c5c8c6}.clo{color:#c5c8c6}.tag{color:#c66}.atn{color:#de935f}.atv{color:#8abeb7}.dec{color:#de935f}.var{color:#c66}.fun{color:#81a2be}}
|
2
libs/css/code-themes/tomorrow.min.css
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/*! Color themes for Google Code Prettify | MIT License | github.com/jmblog/color-themes-for-google-code-prettify */
|
||||||
|
.prettyprint{background:#f6f8fa!important;font-family:Menlo,Bitstream Vera Sans Mono,DejaVu Sans Mono,Monaco,Consolas,monospace;border:0!important}.pln{color:#4d4d4c}ol.linenums{margin-top:0;margin-bottom:0;color:#8e908c}li.L0,li.L1,li.L2,li.L3,li.L4,li.L5,li.L6,li.L7,li.L8,li.L9{padding-left:1em;background-color:#f6f8fa;list-style-type:decimal}@media screen{.str{color:#718c00}.kwd{color:#8959a8}.com{color:#8e908c}.typ{color:#4271ae}.lit{color:#f5871f}.pun{color:#4d4d4c}.opn{color:#4d4d4c}.clo{color:#4d4d4c}.tag{color:#c82829}.atn{color:#f5871f}.atv{color:#3e999f}.dec{color:#f5871f}.var{color:#c82829}.fun{color:#4271ae}}
|
2
libs/css/codemirror.min.css
vendored
Normal file
BIN
libs/css/fonts/element-icons.ttf
Normal file
BIN
libs/css/fonts/element-icons.woff
Normal file
1
libs/css/index.css
Normal file
36
libs/css/show-hint.css
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
.CodeMirror-hints {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 10;
|
||||||
|
overflow: hidden;
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
padding: 2px;
|
||||||
|
|
||||||
|
-webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
|
||||||
|
-moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
|
||||||
|
box-shadow: 2px 3px 5px rgba(0,0,0,.2);
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid silver;
|
||||||
|
|
||||||
|
background: white;
|
||||||
|
font-size: 90%;
|
||||||
|
font-family: monospace;
|
||||||
|
|
||||||
|
max-height: 20em;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-hint {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 4px;
|
||||||
|
border-radius: 2px;
|
||||||
|
white-space: pre;
|
||||||
|
color: black;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.CodeMirror-hint-active {
|
||||||
|
background: #08f;
|
||||||
|
color: white;
|
||||||
|
}
|
104
libs/css/style-mirror.css
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Name: Base16 Default Light
|
||||||
|
Author: Chris Kempson (http://chriskempson.com)
|
||||||
|
|
||||||
|
CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror)
|
||||||
|
Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16)
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
.cm-s-style-mirror.CodeMirror {
|
||||||
|
background: #f5f5f5;
|
||||||
|
color: #444;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 20px;
|
||||||
|
line-height: 25px;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror div.CodeMirror-selected {
|
||||||
|
background: #e0e0e0;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror .CodeMirror-line::selection,
|
||||||
|
.cm-s-style-mirror .CodeMirror-line > span::selection,
|
||||||
|
.cm-s-style-mirror .CodeMirror-line > span > span::selection {
|
||||||
|
background: #e0e0e0;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror .CodeMirror-line::-moz-selection,
|
||||||
|
.cm-s-style-mirror .CodeMirror-line > span::-moz-selection,
|
||||||
|
.cm-s-style-mirror .CodeMirror-line > span > span::-moz-selection {
|
||||||
|
background: #e0e0e0;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror .CodeMirror-gutters {
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-right: 0px;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror .CodeMirror-guttermarker {
|
||||||
|
color: #ac4142;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror .CodeMirror-guttermarker-subtle {
|
||||||
|
color: #b0b0b0;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror .CodeMirror-linenumber {
|
||||||
|
color: #b0b0b0;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror .CodeMirror-cursor {
|
||||||
|
border-left: 1px solid #505050;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-style-mirror span.cm-comment {
|
||||||
|
color:green;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror span.cm-atom {
|
||||||
|
color: #aa759f;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror span.cm-number {
|
||||||
|
color: #aa759f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-style-mirror span.cm-property,
|
||||||
|
.cm-s-style-mirror span.cm-attribute {
|
||||||
|
color: #90a959;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror span.cm-keyword {
|
||||||
|
color: #023a52;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror span.cm-string {
|
||||||
|
color: #e46918;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-style-mirror span.cm-variable {
|
||||||
|
color: #90a959;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror span.cm-variable-2 {
|
||||||
|
color: #00695f;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror span.cm-variable-3 {
|
||||||
|
color: #2e6e8a;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror span.cm-def {
|
||||||
|
color: #d28445;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror span.cm-bracket {
|
||||||
|
color: #202020;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror span.cm-tag {
|
||||||
|
color:#000;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror span.cm-link {
|
||||||
|
color: #b26a00;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror span.cm-error {
|
||||||
|
/* background: #ac4142;
|
||||||
|
color: #f5f5f5; */
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-style: wavy;
|
||||||
|
text-decoration-color: #df8d8e;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror .CodeMirror-activeline-background {
|
||||||
|
background: #dddcdc;
|
||||||
|
}
|
||||||
|
.cm-s-style-mirror .CodeMirror-matchingbracket {
|
||||||
|
color: rgb(32,32,32) !important;
|
||||||
|
background-color: rgba(0,0,0,0.1) !important;
|
||||||
|
}
|
||||||
|
|
2
libs/css/xq-light.min.css
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
.cm-s-xq-light span.cm-keyword{line-height:1em;font-weight:700;color:#5a5cad}.cm-s-xq-light span.cm-atom{color:#6c8cd5}.cm-s-xq-light span.cm-number{color:#164}.cm-s-xq-light span.cm-def{text-decoration:underline}.cm-s-xq-light span.cm-variable{color:#000}.cm-s-xq-light span.cm-variable-2{color:#000}.cm-s-xq-light span.cm-type,.cm-s-xq-light span.cm-variable-3{color:#000}.cm-s-xq-light span.cm-comment{color:#0080ff;font-style:italic}.cm-s-xq-light span.cm-string{color:red}.cm-s-xq-light span.cm-meta{color:#ff0}.cm-s-xq-light span.cm-qualifier{color:grey}.cm-s-xq-light span.cm-builtin{color:#7ea656}.cm-s-xq-light span.cm-bracket{color:#cc7}.cm-s-xq-light span.cm-tag{color:#3f7f7f}.cm-s-xq-light span.cm-attribute{color:#7f007f}.cm-s-xq-light span.cm-error{color:red}.cm-s-xq-light .CodeMirror-activeline-background{background:#e8f2ff}.cm-s-xq-light .CodeMirror-matchingbracket{outline:1px solid grey;color:#000!important;background:#ff0}
|
||||||
|
/*# sourceMappingURL=xq-light.min.css.map */
|