Added CJK support + MathJax display

This commit is contained in:
NGPixel 2017-04-14 14:15:11 -04:00
parent 8daa772c61
commit fe313baf67
13 changed files with 141 additions and 35 deletions

View File

@ -4,57 +4,60 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## Unreleased ## Unreleased
### Added ### Added
- Configuration Wizard: Added Public Access option - **Auth**: Azure AD authentication provider is now available
- Auth: Azure AD authentication provider is now available - **Auth**: Can now specify Read Access by default for all providers (except Local)
- Auth: Can now specify Read Access by default for all providers (except Local) - **View**: MathML and TeX math equations support
- Navigation: All Pages section - **Configuration Wizard**: Added Public Access option
- **Navigation**: All Pages section
### Changed ### Changed
- Auth: Provider Strategies are now only loaded if enabled - **Auth**: Provider Strategies are now only loaded if enabled
### Fixed ### Fixed
- UI: Scrollbar is no longer always shown in code blocks - **Configuration Wizard**: Git version detection no longer fails on MacOS
- Init: Malformed config file is now being reported correctly - **Init**: Malformed config file is now being reported correctly
- **UI**: Scrollbar is no longer always shown in code blocks
- **Misc**: CJK (Chinese, Japanese & Korean) characters are now fully supported for pages, content and uploads
## [v1.0.0-beta.10] - 2017-04-08 ## [v1.0.0-beta.10] - 2017-04-08
### Added ### Added
- Installation: Wiki.js can now install via local tarball - **Installation**: Wiki.js can now install via local tarball
- Installation: RAM check during install to prevent crashing due to low memory - **Installation**: RAM check during install to prevent crashing due to low memory
### Changed ### Changed
- Updated dependencies + snyk policy - Updated dependencies + snyk policy
### Fixed ### Fixed
- UI: Code blocks longer than page width are now displayed with scrollbars - **UI**: Code blocks longer than page width are now displayed with scrollbars
- Configuration Wizard: Git version check no longer fails if between 2.7.4 and 2.11.0 - **Configuration Wizard**: Git version check no longer fails if between 2.7.4 and 2.11.0
- Init: Admin account is no longer attempted to be created during init - **Init**: Admin account is no longer attempted to be created during init
## [v1.0.0-beta.9] - 2017-04-05 ## [v1.0.0-beta.9] - 2017-04-05
### Added ### Added
- Interactive setup - Interactive setup
- Auth: GitHub and Slack authentication providers are now available - **Auth**: GitHub and Slack authentication providers are now available
- Auth: LDAP authentication provider is now available - **Auth**: LDAP authentication provider is now available
- Logs: Support for the logging services: Bugsnag, Loggly, Papertrail, Rollbar and Sentry - **Logs**: Support for the logging services: Bugsnag, Loggly, Papertrail, Rollbar and Sentry
- Config: Can now use ENV variable to specify DB connection string ($VARNAME as db value in config.yml) - **Config**: Can now use ENV variable to specify DB connection string ($VARNAME as db value in config.yml)
### Changed ### Changed
- Native Compilation Removal: Replaced farmhash with md5 - **Native Compilation Removal**: Replaced farmhash with md5
- Native Compilation Removal: Replaced leveldown with memdown - **Native Compilation Removal**: Replaced leveldown with memdown
- Native Compilation Removal: Replaced sharp with jimp - **Native Compilation Removal**: Replaced sharp with jimp
- Sidebar: Contents is now Page Contents - **Sidebar**: Contents is now Page Contents
- Sidebar: Start is now Top of Page - **Sidebar**: Start is now Top of Page
- UI: Content headers are now showing an anchor icon instead of a # - **UI**: Content headers are now showing an anchor icon instead of a #
- Dev: Replaced Gulp with Fuse-box - **Dev**: Replaced Gulp with Fuse-box
### Fixed ### Fixed
- Auth: Authentication would fail if email has uppercase chars and provider callback is in lowercase - **Auth**: Authentication would fail if email has uppercase chars and provider callback is in lowercase
- Markdown: Fixed potential crash on markdown processing of video links - **Markdown**: Fixed potential crash on markdown processing of video links
- Search: Search index should now update upon article creation - **Search**: Search index should now update upon article creation
- Search: Search results are no longer duplicated upon article update - **Search**: Search results are no longer duplicated upon article update
- UI: Missing icons on login page - **UI**: Missing icons on login page
- UI: Image alignement center and right should now behave correctly - **UI**: Image alignement center and right should now behave correctly
- Uploads: Error notification when upload is too large for server - **Uploads**: Error notification when upload is too large for server
- Uploads: Fix uploads and temp-uploads folder permissions on unix-based systems - **Uploads**: Fix uploads and temp-uploads folder permissions on unix-based systems
## [v1.0.0-beta.8] - 2017-02-19 ## [v1.0.0-beta.8] - 2017-02-19
### Added ### Added

View File

@ -53,6 +53,8 @@ defaults:
signature: signature:
name: Wiki name: Wiki
email: wiki@example.com email: wiki@example.com
features:
mathjax: true
externalLogging: externalLogging:
bugsnap: false bugsnap: false
loggly: false loggly: false

9
app/regex.js Normal file
View File

@ -0,0 +1,9 @@
'use strict'
module.exports = {
arabic: /([\u0600-\u06ff]|[\u0750-\u077f]|[\ufb50-\ufc3f]|[\ufe70-\ufefc])/,
cjk: /([\u4E00-\u9FBF]|[\u3040-\u309F\u30A0-\u30FF]|[ㄱ-ㅎ가-힣ㅏ-ㅣ])/,
youtube: /(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|&v(?:i)?=))([^#&?]*).*/,
vimeo: /vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/(?:[^/]*)\/videos\/|album\/(?:\d+)\/video\/|)(\d+)(?:$|\/|\?)/,
dailymotion: /(?:dailymotion\.com(?:\/embed)?(?:\/video|\/hub)|dai\.ly)\/([0-9a-z]+)(?:[-_0-9a-zA-Z]+(?:#video=)?([a-z0-9]+)?)?/
}

View File

@ -1,11 +1,27 @@
'use strict' 'use strict'
import $ from 'jquery' import $ from 'jquery'
import MathJax from 'mathjax'
module.exports = (alerts) => { module.exports = (alerts) => {
if ($('#page-type-view').length) { if ($('#page-type-view').length) {
let currentBasePath = ($('#page-type-view').data('entrypath') !== 'home') ? $('#page-type-view').data('entrypath') : '' let currentBasePath = ($('#page-type-view').data('entrypath') !== 'home') ? $('#page-type-view').data('entrypath') : ''
// MathJax Render
MathJax.Hub.Config({
jax: ['input/TeX', 'input/MathML', 'output/SVG'],
extensions: ['tex2jax.js', 'mml2jax.js'],
TeX: {
extensions: ['AMSmath.js', 'AMSsymbols.js', 'noErrors.js', 'noUndefined.js']
},
SVG: {
scale: 120,
font: 'STIX-Web'
},
showMathMenu: false
})
require('../modals/create.js')(currentBasePath) require('../modals/create.js')(currentBasePath)
require('../modals/move.js')(currentBasePath, alerts) require('../modals/move.js')(currentBasePath, alerts)
} }

View File

@ -131,6 +131,14 @@ git:
name: Marty name: Marty
email: marty@example.com email: marty@example.com
# ---------------------------------------------------------------------
# Features
# ---------------------------------------------------------------------
# You can enable / disable specific features below
features:
mathjax: true
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
# External Logging # External Logging
# --------------------------------------------------------------------- # ---------------------------------------------------------------------

View File

@ -10,7 +10,7 @@ const fs = Promise.promisifyAll(require('fs-extra'))
const path = require('path') const path = require('path')
const _ = require('lodash') const _ = require('lodash')
const validPathRe = new RegExp('^([a-z0-9\\/-]+\\.[a-z0-9]+)$') const validPathRe = new RegExp('^(([a-z0-9/-]|' + appdata.regex.cjk.source + ')+\\.[a-z0-9]+)$')
const validPathThumbsRe = new RegExp('^([a-z0-9]+\\.png)$') const validPathThumbsRe = new RegExp('^([a-z0-9]+\\.png)$')
// ========================================== // ==========================================

55
fuse.js
View File

@ -59,6 +59,10 @@ const SHIMS = {
jquery: { jquery: {
source: 'node_modules/jquery/dist/jquery.js', source: 'node_modules/jquery/dist/jquery.js',
exports: '$' exports: '$'
},
mathjax: {
source: 'node_modules/mathjax/MathJax.js',
exports: 'MathJax'
} }
} }
@ -69,6 +73,9 @@ const SHIMS = {
console.info(colors.white('└── ') + colors.green('Running global tasks...')) console.info(colors.white('└── ') + colors.green('Running global tasks...'))
let globalTasks = Promise.mapSeries([ let globalTasks = Promise.mapSeries([
/**
* ACE Modes
*/
() => { () => {
return fs.accessAsync('./assets/js/ace').then(() => { return fs.accessAsync('./assets/js/ace').then(() => {
console.info(colors.white(' └── ') + colors.magenta('ACE modes directory already exists. Task aborted.')) console.info(colors.white(' └── ') + colors.magenta('ACE modes directory already exists. Task aborted.'))
@ -89,6 +96,54 @@ let globalTasks = Promise.mapSeries([
throw err throw err
} }
}) })
},
/**
* MathJax
*/
() => {
return fs.accessAsync('./assets/js/mathjax').then(() => {
console.info(colors.white(' └── ') + colors.magenta('MathJax directory already exists. Task aborted.'))
return true
}).catch(err => {
if (err.code === 'ENOENT') {
console.info(colors.white(' └── ') + colors.green('Copy MathJax dependencies to assets...'))
return fs.ensureDirAsync('./assets/js/mathjax').then(() => {
return fs.copyAsync('./node_modules/mathjax', './assets/js/mathjax', { filter: (src, dest) => {
let srcNormalized = src.replace(/\\/g, '/')
let shouldCopy = false
console.log(srcNormalized)
_.forEach([
'/node_modules/mathjax',
'/node_modules/mathjax/jax',
'/node_modules/mathjax/jax/input',
'/node_modules/mathjax/jax/output'
], chk => {
if (srcNormalized.endsWith(chk)) {
shouldCopy = true
}
})
_.forEach([
'/node_modules/mathjax/extensions',
'/node_modules/mathjax/MathJax.js',
'/node_modules/mathjax/jax/element',
'/node_modules/mathjax/jax/input/MathML',
'/node_modules/mathjax/jax/input/TeX',
'/node_modules/mathjax/jax/output/SVG'
], chk => {
if (srcNormalized.indexOf(chk) > 0) {
shouldCopy = true
}
})
if (shouldCopy && srcNormalized.indexOf('/fonts/') > 0 && srcNormalized.indexOf('/STIX-Web') <= 1) {
shouldCopy = false
}
return shouldCopy
}})
})
} else {
throw err
}
})
} }
], f => { return f() }) ], f => { return f() })

View File

@ -13,7 +13,8 @@ const _ = require('lodash')
module.exports = (confPaths) => { module.exports = (confPaths) => {
confPaths = _.defaults(confPaths, { confPaths = _.defaults(confPaths, {
config: './config.yml', config: './config.yml',
data: './app/data.yml' data: './app/data.yml',
dataRegex: '../app/regex.js'
}) })
let appconfig = {} let appconfig = {}
@ -22,6 +23,7 @@ module.exports = (confPaths) => {
try { try {
appconfig = yaml.safeLoad(fs.readFileSync(confPaths.config, 'utf8')) appconfig = yaml.safeLoad(fs.readFileSync(confPaths.config, 'utf8'))
appdata = yaml.safeLoad(fs.readFileSync(confPaths.data, 'utf8')) appdata = yaml.safeLoad(fs.readFileSync(confPaths.data, 'utf8'))
appdata.regex = require(confPaths.dataRegex)
} catch (ex) { } catch (ex) {
console.error(ex) console.error(ex)
process.exit(1) process.exit(1)

View File

@ -5,6 +5,7 @@ const path = require('path')
const fs = Promise.promisifyAll(require('fs-extra')) const fs = Promise.promisifyAll(require('fs-extra'))
const _ = require('lodash') const _ = require('lodash')
const crypto = require('crypto') const crypto = require('crypto')
const qs = require('querystring')
/** /**
* Entries Model * Entries Model
@ -163,7 +164,8 @@ module.exports = {
* @return {String} Safe entry path * @return {String} Safe entry path
*/ */
parsePath (urlPath) { parsePath (urlPath) {
let wlist = new RegExp('[^a-z0-9/-]', 'g') urlPath = qs.unescape(urlPath)
let wlist = new RegExp('(?!([^a-z0-9]|' + appdata.regex.cjk.source + '|[/-]))', 'g')
urlPath = _.toLower(urlPath).replace(wlist, '') urlPath = _.toLower(urlPath).replace(wlist, '')

View File

@ -152,7 +152,7 @@ module.exports = {
*/ */
validateUploadsFilename (f, fld, isImage) { validateUploadsFilename (f, fld, isImage) {
let fObj = path.parse(f) let fObj = path.parse(f)
let fname = _.chain(fObj.name).trim().toLower().kebabCase().value().replace(/[^a-z0-9-]+/g, '') let fname = _.chain(fObj.name).trim().toLower().kebabCase().value().replace(new RegExp('(?!([^a-z0-9-]|' + appdata.regex.cjk.source + '))', 'g'), '')
let fext = _.toLower(fObj.ext) let fext = _.toLower(fObj.ext)
if (isImage && !_.includes(['.jpg', '.jpeg', '.png', '.gif', '.webp'], fext)) { if (isImage && !_.includes(['.jpg', '.jpeg', '.png', '.gif', '.webp'], fext)) {

View File

@ -51,6 +51,11 @@ var mkdown = md({
}) })
.use(mdAttrs) .use(mdAttrs)
if (appconfig) {
const mdMathjax = require('markdown-it-mathjax')
mkdown.use(mdMathjax())
}
// Rendering rules // Rendering rules
mkdown.renderer.rules.emoji = function (token, idx) { mkdown.renderer.rules.emoji = function (token, idx) {

View File

@ -82,6 +82,7 @@
"markdown-it-expand-tabs": "^1.0.11", "markdown-it-expand-tabs": "^1.0.11",
"markdown-it-external-links": "0.0.6", "markdown-it-external-links": "0.0.6",
"markdown-it-footnote": "^3.0.1", "markdown-it-footnote": "^3.0.1",
"markdown-it-mathjax": "^2.0.0",
"markdown-it-task-lists": "^2.0.0", "markdown-it-task-lists": "^2.0.0",
"memdown": "^1.2.4", "memdown": "^1.2.4",
"mime-types": "^2.1.15", "mime-types": "^2.1.15",
@ -139,6 +140,7 @@
"jquery-contextmenu": "^2.4.4", "jquery-contextmenu": "^2.4.4",
"jquery-simple-upload": "^1.0.0", "jquery-simple-upload": "^1.0.0",
"jquery-smooth-scroll": "^2.0.0", "jquery-smooth-scroll": "^2.0.0",
"mathjax": "^2.7.0",
"node-sass": "latest", "node-sass": "latest",
"nodemon": "latest", "nodemon": "latest",
"pug-lint": "latest", "pug-lint": "latest",

View File

@ -18,6 +18,8 @@ html
link(rel='manifest', href='/manifest.json') link(rel='manifest', href='/manifest.json')
// JS / CSS // JS / CSS
script(type='text/javascript').
window.MathJax = { root:"/js/mathjax" }
script(type='text/javascript', src='/js/bundle.min.js') script(type='text/javascript', src='/js/bundle.min.js')
block head block head