From 19dad9dfc8e2bcb5dd97d37ef6c79396cac460ee Mon Sep 17 00:00:00 2001 From: Yukai Huang Date: Fri, 31 Jan 2020 17:51:14 +0800 Subject: [PATCH] Lazy load dicts, support cdn, config webpack Signed-off-by: Yukai Huang --- lib/status/index.js | 3 +- package.json | 3 + public/js/lib/common/constant.ejs | 2 + public/js/lib/editor/index.js | 22 ++++-- public/js/lib/editor/spellcheck.js | 107 ++++++++++++++++++++++------ public/js/lib/editor/statusbar.html | 4 -- webpack.common.js | 17 ++++- yarn.lock | 15 ++++ 8 files changed, 141 insertions(+), 32 deletions(-) diff --git a/lib/status/index.js b/lib/status/index.js index 0a323d84..5c571156 100644 --- a/lib/status/index.js +++ b/lib/status/index.js @@ -24,7 +24,8 @@ exports.getConfig = (req, res) => { DROPBOX_APP_KEY: config.dropbox.appKey, allowedUploadMimeTypes: config.allowedUploadMimeTypes, defaultUseHardbreak: config.defaultUseHardbreak, - linkifyHeaderStyle: config.linkifyHeaderStyle + linkifyHeaderStyle: config.linkifyHeaderStyle, + useCDN: config.useCDN } res.set({ 'Cache-Control': 'private', // only cache by client diff --git a/package.json b/package.json index 2d504e1d..f1d60023 100644 --- a/package.json +++ b/package.json @@ -165,6 +165,9 @@ "babel-runtime": "~6.26.0", "copy-webpack-plugin": "~4.5.2", "css-loader": "~1.0.0", + "dictionary-de": "^2.0.3", + "dictionary-de-at": "^2.0.3", + "dictionary-de-ch": "^2.0.3", "doctoc": "~1.4.0", "ejs-loader": "~0.3.1", "exports-loader": "~0.7.0", diff --git a/public/js/lib/common/constant.ejs b/public/js/lib/common/constant.ejs index 537985f4..57438912 100644 --- a/public/js/lib/common/constant.ejs +++ b/public/js/lib/common/constant.ejs @@ -11,3 +11,5 @@ window.defaultUseHardbreak = <%- defaultUseHardbreak %> window.linkifyHeaderStyle = '<%- linkifyHeaderStyle %>' window.DROPBOX_APP_KEY = '<%- DROPBOX_APP_KEY %>' + +window.USE_CDN = <%- useCDN %> diff --git a/public/js/lib/editor/index.js b/public/js/lib/editor/index.js index 7d796da2..9363eeb7 100644 --- a/public/js/lib/editor/index.js +++ b/public/js/lib/editor/index.js @@ -7,7 +7,7 @@ import config from './config' import statusBarTemplate from './statusbar.html' import toolBarTemplate from './toolbar.html' import './markdown-lint' -import CodeMirrorSpellChecker, { supportLanguages } from './spellcheck' +import CodeMirrorSpellChecker, { supportLanguages, supportLanguageCodes } from './spellcheck' import { initTableEditor } from './table-editor' import { availableThemes } from './constants' @@ -548,7 +548,7 @@ export default class Editor { return } - if (!supportLanguages.includes(lang)) { + if (!supportLanguageCodes.includes(lang)) { return } @@ -558,7 +558,21 @@ export default class Editor { this.spellchecker.setDictLang(lang) } + getExistingSpellcheckLang () { + const cookieSpellcheck = Cookies.get('spellcheck') + + if (cookieSpellcheck) { + return cookieSpellcheck === 'false' ? undefined : cookieSpellcheck + } else { + return undefined + } + } + setSpellcheck () { + this.statusSpellcheck.find('ul.dropdown-menu').append(supportLanguages.map(lang => { + return $(`
  • ${lang.name}
  • `) + })) + const cookieSpellcheck = Cookies.get('spellcheck') if (cookieSpellcheck) { let mode = null @@ -567,7 +581,7 @@ export default class Editor { mode = defaultEditorMode } else { mode = 'spell-checker' - if (supportLanguages.includes(cookieSpellcheck)) { + if (supportLanguageCodes.includes(cookieSpellcheck)) { lang = cookieSpellcheck } } @@ -740,7 +754,7 @@ export default class Editor { placeholder: "← Start by entering a title here\n===\nVisit /features if you don't know what to do.\nHappy hacking :)" }) - this.spellchecker = new CodeMirrorSpellChecker(CodeMirror) + this.spellchecker = new CodeMirrorSpellChecker(CodeMirror, this.getExistingSpellcheckLang()) this.tableEditor = initTableEditor(this.editor) return this.editor diff --git a/public/js/lib/editor/spellcheck.js b/public/js/lib/editor/spellcheck.js index 24fb9c49..11d30f2f 100644 --- a/public/js/lib/editor/spellcheck.js +++ b/public/js/lib/editor/spellcheck.js @@ -5,26 +5,58 @@ import Typo from 'typo-js' import { serverurl } from '../config' -const dictionaryDownloadUrls = { - en_US: { - aff: `${serverurl}/vendor/codemirror-spell-checker/en_US.aff`, - dic: `${serverurl}/vendor/codemirror-spell-checker/en_US.dic` +export const supportLanguages = [ + { + name: 'English (United States)', + value: 'en_US', + aff: { + url: `${serverurl}/vendor/codemirror-spell-checker/en_US.aff`, + cdnUrl: `${serverurl}/vendor/codemirror-spell-checker/en_US.aff` + }, + dic: { + url: `${serverurl}/vendor/codemirror-spell-checker/en_US.dic`, + cdnUrl: `${serverurl}/vendor/codemirror-spell-checker/en_US.dic` + } }, - de: { - aff: 'https://rawcdn.githack.com/wooorm/dictionaries/143091715eebbbdfa0e8936e117f9182514eebe6/dictionaries/de/index.aff', - dic: 'https://rawcdn.githack.com/wooorm/dictionaries/143091715eebbbdfa0e8936e117f9182514eebe6/dictionaries/de/index.dic' + { + name: 'German', + value: 'de', + aff: { + url: `${serverurl}/build/dictionary-de/index.aff`, + cdnUrl: `https://cdn.jsdelivr.net/npm/dictionary-de@2.0.3/index.aff` + }, + dic: { + url: `${serverurl}/build/dictionary-de/index.dic`, + cdnUrl: `https://cdn.jsdelivr.net/npm/dictionary-de@2.0.3/index.dic` + } }, - de_AT: { - aff: 'https://rawcdn.githack.com/wooorm/dictionaries/143091715eebbbdfa0e8936e117f9182514eebe6/dictionaries/de-AT/index.aff', - dic: 'https://rawcdn.githack.com/wooorm/dictionaries/143091715eebbbdfa0e8936e117f9182514eebe6/dictionaries/de-AT/index.dic' + { + name: 'German (Austria)', + value: 'de_AT', + aff: { + url: `${serverurl}/build/dictionary-de-at/index.aff`, + cdnUrl: `https://cdn.jsdelivr.net/npm/dictionary-de-at@2.0.3/index.aff` + }, + dic: { + url: `${serverurl}/build/dictionary-de-at/index.dic`, + cdnUrl: `https://cdn.jsdelivr.net/npm/dictionary-de-at@2.0.3/index.dic` + } }, - de_CH: { - aff: 'https://rawcdn.githack.com/wooorm/dictionaries/143091715eebbbdfa0e8936e117f9182514eebe6/dictionaries/de-CH/index.aff', - dic: 'https://rawcdn.githack.com/wooorm/dictionaries/143091715eebbbdfa0e8936e117f9182514eebe6/dictionaries/de-CH/index.dic' + { + name: 'German (Switzerland)', + value: 'de_CH', + aff: { + url: `${serverurl}/build/dictionary-de-ch/index.aff`, + cdnUrl: `https://cdn.jsdelivr.net/npm/dictionary-de-ch@2.0.3/index.aff` + }, + dic: { + url: `${serverurl}/build/dictionary-de-ch/index.dic`, + cdnUrl: `https://cdn.jsdelivr.net/npm/dictionary-de-ch@2.0.3/index.dic` + } } -} +] -export const supportLanguages = Object.keys(dictionaryDownloadUrls) +export const supportLanguageCodes = supportLanguages.map(lang => lang.value) function request (url) { return new Promise(resolve => { @@ -59,20 +91,51 @@ function createTypo (lang, affData, dicData) { const typoMap = new Map() +let fetching = false async function findOrCreateTypoInstance (lang) { + if (!lang) { + return + } + // find existing typo instance let typo = typoMap.get(lang) if (typo) { return typo } - const [affData, dicData] = await mapSeriesP([ - dictionaryDownloadUrls[lang].aff, - dictionaryDownloadUrls[lang].dic - ], request) + let dict = supportLanguages.find(l => l.value === lang) - typo = createTypo(lang, affData, dicData) - typoMap.set(lang, typo) + if (!dict) { + console.error(`Dictionary not found for "${lang}"\n Fallback to default English spellcheck`) + dict = supportLanguages[0] + } + + let affUrl + let dicUrl + if (window.USE_CDN) { + affUrl = dict.aff.cdnUrl + dicUrl = dict.dic.cdnUrl + } else { + affUrl = dict.aff.url + dicUrl = dict.dic.url + } + + if (fetching) { + return typo + } + + try { + fetching = true + + const [affData, dicData] = await mapSeriesP([affUrl, dicUrl], request) + + typo = createTypo(lang, affData, dicData) + typoMap.set(lang, typo) + } catch (err) { + console.error(err) + } finally { + fetching = false + } return typo } @@ -82,7 +145,7 @@ class CodeMirrorSpellChecker { * @param {CodeMirror} cm * @param {string} lang */ - constructor (cm, lang = 'en_US') { + constructor (cm, lang) { // Verify if (typeof cm !== 'function' || typeof cm.defineMode !== 'function') { console.log( diff --git a/public/js/lib/editor/statusbar.html b/public/js/lib/editor/statusbar.html index ba718108..88f8c1f4 100644 --- a/public/js/lib/editor/statusbar.html +++ b/public/js/lib/editor/statusbar.html @@ -45,10 +45,6 @@
    diff --git a/webpack.common.js b/webpack.common.js index b7aa21bb..ac4b538e 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -165,7 +165,22 @@ module.exports = { context: path.join(__dirname, 'node_modules/reveal.js'), from: 'plugin', to: 'reveal.js/plugin' - } + }, + { + context: path.join(__dirname, 'node_modules/dictionary-de'), + from: '*', + to: 'dictionary-de/' + }, + { + context: path.join(__dirname, 'node_modules/dictionary-de-at'), + from: '*', + to: 'dictionary-de-at/' + }, + { + context: path.join(__dirname, 'node_modules/dictionary-de-ch'), + from: '*', + to: 'dictionary-de-ch/' + }, ]), new MiniCssExtractPlugin() ], diff --git a/yarn.lock b/yarn.lock index faa07469..22fb8e04 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4333,6 +4333,21 @@ diagnostics@^1.1.1: enabled "1.0.x" kuler "1.0.x" +dictionary-de-at@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dictionary-de-at/-/dictionary-de-at-2.0.3.tgz#78f31d0cd8ca7c7d5ba48fdefb7a7bd3f05e11ca" + integrity sha512-unbay9PPM75yZ0RPnqSD/PADpZj7/vPDVeau2jTsVPFKwhoZGJTBVLD2wCaIkhS6tyVsNOboo1VYjzOCOit2ww== + +dictionary-de-ch@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dictionary-de-ch/-/dictionary-de-ch-2.0.3.tgz#1727413a1eb35eb78e7fe15b5b7a742fd650f0c3" + integrity sha512-+eqpz5j8WONSzxmc4avCN4XX/6q5+J6JfWz2AaluZIOVNgXPxUjXBhKS73+nRhM3nE1pGeRMqkyZevTQWgYTTw== + +dictionary-de@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dictionary-de/-/dictionary-de-2.0.3.tgz#df50c749fddbff601f5bd044aef4622a365a15b2" + integrity sha512-fbNcCIjDrdNvu7DzMzkOY77vIaGqiDQqf9vtwGud1fcSxVWwX6EdtHcosmgG7AA10u3QgDVkymMaX9mr3elwRw== + diff-match-patch@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.4.tgz#6ac4b55237463761c4daf0dc603eb869124744b1"