Skip to content

Commit 6736a08

Browse files
roottoolRokt33r
authored andcommitted
Fixes that TOC hasn't id attribute when the title is all 2-byte characters (#2994)
* Fix: 2-byte character support of slug * Fix: Decodes slug to display slug * Fix: Removed a logic of replaceDiacritics * Fix: Fixed slugify to pass tests * Fix: Fixed not to remove underscore * Adds the test for slugify.js * Fix: Fix to jump to heading * Added a comment * Fix: Created click event only linking to heading * Fix: Fix to use handleLinkClick(e) * Fix: Changed the regex rule * Fix: Changed the regex rule of extractId
1 parent 49c75e3 commit 6736a08

File tree

4 files changed

+79
-21
lines changed

4 files changed

+79
-21
lines changed

browser/components/MarkdownPreview.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -895,6 +895,12 @@ export default class MarkdownPreview extends React.Component {
895895
this.setImgOnClickEventHelper(img, rect)
896896
imgObserver.observe(parentEl, config)
897897
}
898+
899+
const aList = markdownPreviewIframe.contentWindow.document.body.querySelectorAll('a')
900+
for (const a of aList) {
901+
a.removeEventListener('click', this.linkClickHandler)
902+
a.addEventListener('click', this.linkClickHandler)
903+
}
898904
}
899905

900906
setImgOnClickEventHelper (img, rect) {
@@ -1023,11 +1029,11 @@ export default class MarkdownPreview extends React.Component {
10231029

10241030
if (!rawHref) return // not checked href because parser will create file://... string for [empty link]()
10251031

1026-
const regexNoteInternalLink = /.*[main.\w]*.html#/
1027-
1028-
if (regexNoteInternalLink.test(href)) {
1029-
const targetId = mdurl.encode(linkHash)
1030-
const targetElement = this.refs.root.contentWindow.document.querySelector(
1032+
const extractId = /(main.html)?#/
1033+
const regexNoteInternalLink = new RegExp(`${extractId.source}(.+)`)
1034+
if (regexNoteInternalLink.test(linkHash)) {
1035+
const targetId = mdurl.encode(linkHash.replace(extractId, ''))
1036+
const targetElement = this.refs.root.contentWindow.document.getElementById(
10311037
targetId
10321038
)
10331039

browser/lib/markdown-toc-generator.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function uniqueSlug (slug, slugs, opts) {
2121
}
2222

2323
function linkify (token) {
24-
token.content = mdlink(token.content, '#' + token.slug)
24+
token.content = mdlink(token.content, `#${decodeURI(token.slug)}`)
2525
return token
2626
}
2727

browser/lib/slugify.js

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
1-
import diacritics from 'diacritics-map'
2-
3-
function replaceDiacritics (str) {
4-
return str.replace(/[À-ž]/g, function (ch) {
5-
return diacritics[ch] || ch
6-
})
7-
}
8-
91
module.exports = function slugify (title) {
10-
let slug = title.trim()
11-
12-
slug = replaceDiacritics(slug)
13-
14-
slug = slug.replace(/[^\w\s-]/g, '').replace(/\s+/g, '-')
15-
16-
return encodeURI(slug).replace(/\-+$/, '')
2+
const slug = encodeURI(
3+
title.trim()
4+
.replace(/^\s+/, '')
5+
.replace(/\s+$/, '')
6+
.replace(/\s+/g, '-')
7+
.replace(/[\]\[\!\'\#\$\%\&\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\{\|\}\~\`]/g, '')
8+
)
9+
10+
return slug
1711
}

tests/lib/slugify-test.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import test from 'ava'
2+
import slugify from 'browser/lib/slugify'
3+
4+
test('alphabet and digit', t => {
5+
const upperAlphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
6+
const lowerAlphabet = 'abcdefghijklmnopqrstuvwxyz'
7+
const digit = '0123456789'
8+
const testCase = upperAlphabet + lowerAlphabet + digit
9+
const decodeSlug = decodeURI(slugify(testCase))
10+
11+
t.true(decodeSlug === testCase)
12+
})
13+
14+
test('should delete unavailable symbols', t => {
15+
const availableSymbols = '_-'
16+
const testCase = availableSymbols + '][!\'#$%&()*+,./:;<=>?@\\^{|}~`'
17+
const decodeSlug = decodeURI(slugify(testCase))
18+
19+
t.true(decodeSlug === availableSymbols)
20+
})
21+
22+
test('should convert from white spaces between words to hyphens', t => {
23+
const testCase = 'This is one'
24+
const expectedString = 'This-is-one'
25+
const decodeSlug = decodeURI(slugify(testCase))
26+
27+
t.true(decodeSlug === expectedString)
28+
})
29+
30+
test('should remove leading white spaces', t => {
31+
const testCase = ' This is one'
32+
const expectedString = 'This-is-one'
33+
const decodeSlug = decodeURI(slugify(testCase))
34+
35+
t.true(decodeSlug === expectedString)
36+
})
37+
38+
test('should remove trailing white spaces', t => {
39+
const testCase = 'This is one '
40+
const expectedString = 'This-is-one'
41+
const decodeSlug = decodeURI(slugify(testCase))
42+
43+
t.true(decodeSlug === expectedString)
44+
})
45+
46+
test('2-byte charactor support', t => {
47+
const testCase = '菠萝芒果テストÀžƁƵ'
48+
const decodeSlug = decodeURI(slugify(testCase))
49+
50+
t.true(decodeSlug === testCase)
51+
})
52+
53+
test('emoji', t => {
54+
const testCase = '🌸'
55+
const decodeSlug = decodeURI(slugify(testCase))
56+
57+
t.true(decodeSlug === testCase)
58+
})

0 commit comments

Comments
 (0)