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"