diff --git a/app/assets/javascripts/discourse-markdown-it/src/features/bbcode-block.js b/app/assets/javascripts/discourse-markdown-it/src/features/bbcode-block.js index 8e847f4d968..763e597ab5d 100644 --- a/app/assets/javascripts/discourse-markdown-it/src/features/bbcode-block.js +++ b/app/assets/javascripts/discourse-markdown-it/src/features/bbcode-block.js @@ -34,15 +34,9 @@ function trailingSpaceOnly(src, start, max) { return true; } -// Easiest case is the closing tag which never has any attributes -const BBCODE_CLOSING_TAG_REGEXP = /^\[\/([-\w]+)\]/i; - -// Old case where we supported attributes without quotation marks -const BBCODE_QUOTE_TAG_REGEXP = /^\[quote=([-\w,: ]+)\]/i; - // Most common quotation marks. // More can be found at https://en.wikipedia.org/wiki/Quotation_mark -const QUOTATION_MARKS = [`""`, `''`, `“”`, `‘’`, `„“`, `‚’`, `«»`, `‹›`]; +const QUOTATION_MARKS = [`""`, `''`, `“”`, `””`, `‘’`, `„“`, `‚’`, `«»`, `‹›`]; const QUOTATION_MARKS_NO_MATCH = QUOTATION_MARKS.map( ([a, b]) => `${a}[^${b}]+${b}` @@ -52,6 +46,15 @@ const QUOTATION_MARKS_WITH_MATCH = QUOTATION_MARKS.map( ([a, b]) => `${a}([^${b}]+)${b}` ).join("|"); +// Easiest case is the closing tag which never has any attributes +const BBCODE_CLOSING_TAG_REGEXP = /^\[\/([-\w]+)\]/i; + +// Old case where we supported attributes without quotation marks +const BBCODE_QUOTE_OR_DETAILS_TAG_REGEXP = new RegExp( + `^\\[(quote|details)=(\\s*[^${QUOTATION_MARKS.join("")}].+?)\\]`, + "i" +); + // This is used to match a **valid** opening tag // NOTE: it does not match the closing bracket "]" because it makes the regexp too slow // due to the backtracking. So we check for the "]" manually. @@ -86,18 +89,18 @@ export function parseBBCodeTag(src, start, max, multiline) { }; } - // CASE 2 - [quote=...] tag (without quotes) - m = BBCODE_QUOTE_TAG_REGEXP.exec(text); + // CASE 2 - [quote=...] or [details=...] tag (without quotes) + m = BBCODE_QUOTE_OR_DETAILS_TAG_REGEXP.exec(text); - if (m && m[0] && m[1]) { + if (m && m[0] && m[1] && m[2]) { if (multiline && !trailingSpaceOnly(src, start + m[0].length, max)) { return null; } return { - tag: "quote", + tag: m[1], length: m[0].length, - attrs: { _default: m[1] }, + attrs: { _default: m[2] }, }; } diff --git a/plugins/discourse-details/test/javascripts/lib/details-cooked-test.js b/plugins/discourse-details/test/javascripts/lib/details-cooked-test.js index c8a999e1c48..f8b22a38688 100644 --- a/plugins/discourse-details/test/javascripts/lib/details-cooked-test.js +++ b/plugins/discourse-details/test/javascripts/lib/details-cooked-test.js @@ -1,23 +1,16 @@ +import { setupTest } from "ember-qunit"; import { module, test } from "qunit"; import { cook } from "discourse/lib/text"; -const opts = { - siteSettings: { - enable_emoji: true, - emoji_set: "twitter", - highlighted_languages: "json|ruby|javascript", - default_code_lang: "auto", - }, - censoredWords: "shucks|whiz|whizzer", - getURL: (url) => url, -}; +module("lib:details-cooked-test", (hooks) => { + setupTest(hooks); -module("lib:details-cooked-test", function () { - test("details", async function (assert) { + test("details", async (assert) => { const testCooked = async (input, expected, text) => { - const cooked = (await cook(input, opts)).toString(); + const cooked = (await cook(input)).toString(); assert.strictEqual(cooked, expected, text); }; + await testCooked( `
Infocoucou
`, `
Infocoucou
`, @@ -25,12 +18,15 @@ module("lib:details-cooked-test", function () { ); await testCooked( - "[details=testing]\ntest\n[/details]", - `
- -testing -

test

-
` + `[details=test'ing all the things]\ntest\n[/details]`, + `
\n\ntest'ing all the things\n

test

\n
`, + "details with spaces and a single quote" + ); + + await testCooked( + `[details=”test'ing all the things”]\ntest\n[/details]`, + `
\n\ntest'ing all the things\n

test

\n
`, + "details surrounded by finnish double quotes" ); }); });