From b0e0b657b4cf44c8ba9709a19e5245f1c5ab0673 Mon Sep 17 00:00:00 2001 From: Blake Erickson Date: Mon, 23 Oct 2023 13:48:56 -0600 Subject: [PATCH] FEATURE: Bundle discourse-footnote plugin into core (#23995) Formerly https://github.com/discourse/discourse-footnote --- .gitignore | 1 + plugins/footnote/README.md | 8 + .../initializers/inline-footnotes.js | 130 ++++++ .../lib/discourse-markdown/footnotes.js | 26 ++ .../assets/stylesheets/footnotes.scss | 106 +++++ .../javascripts/markdown-it-footnote.js | 382 ++++++++++++++++++ plugins/footnote/config/locales/server.ar.yml | 10 + plugins/footnote/config/locales/server.be.yml | 7 + plugins/footnote/config/locales/server.bg.yml | 7 + .../footnote/config/locales/server.bs_BA.yml | 7 + plugins/footnote/config/locales/server.ca.yml | 7 + plugins/footnote/config/locales/server.cs.yml | 7 + plugins/footnote/config/locales/server.da.yml | 7 + plugins/footnote/config/locales/server.de.yml | 10 + plugins/footnote/config/locales/server.el.yml | 7 + plugins/footnote/config/locales/server.en.yml | 4 + .../footnote/config/locales/server.en_GB.yml | 7 + plugins/footnote/config/locales/server.es.yml | 10 + plugins/footnote/config/locales/server.et.yml | 7 + .../footnote/config/locales/server.fa_IR.yml | 7 + plugins/footnote/config/locales/server.fi.yml | 10 + plugins/footnote/config/locales/server.fr.yml | 10 + plugins/footnote/config/locales/server.gl.yml | 7 + plugins/footnote/config/locales/server.he.yml | 10 + plugins/footnote/config/locales/server.hr.yml | 7 + plugins/footnote/config/locales/server.hu.yml | 7 + plugins/footnote/config/locales/server.hy.yml | 7 + plugins/footnote/config/locales/server.id.yml | 7 + plugins/footnote/config/locales/server.it.yml | 10 + plugins/footnote/config/locales/server.ja.yml | 10 + plugins/footnote/config/locales/server.ko.yml | 7 + plugins/footnote/config/locales/server.lt.yml | 7 + plugins/footnote/config/locales/server.lv.yml | 7 + .../footnote/config/locales/server.nb_NO.yml | 7 + plugins/footnote/config/locales/server.nl.yml | 10 + .../footnote/config/locales/server.pl_PL.yml | 10 + plugins/footnote/config/locales/server.pt.yml | 7 + .../footnote/config/locales/server.pt_BR.yml | 10 + plugins/footnote/config/locales/server.ro.yml | 7 + plugins/footnote/config/locales/server.ru.yml | 10 + plugins/footnote/config/locales/server.sk.yml | 7 + plugins/footnote/config/locales/server.sl.yml | 7 + plugins/footnote/config/locales/server.sq.yml | 7 + plugins/footnote/config/locales/server.sr.yml | 7 + plugins/footnote/config/locales/server.sv.yml | 10 + plugins/footnote/config/locales/server.sw.yml | 7 + plugins/footnote/config/locales/server.te.yml | 7 + plugins/footnote/config/locales/server.th.yml | 7 + .../footnote/config/locales/server.tr_TR.yml | 10 + plugins/footnote/config/locales/server.uk.yml | 7 + plugins/footnote/config/locales/server.ur.yml | 7 + plugins/footnote/config/locales/server.vi.yml | 7 + .../footnote/config/locales/server.zh_CN.yml | 10 + .../footnote/config/locales/server.zh_TW.yml | 7 + plugins/footnote/config/settings.yml | 7 + plugins/footnote/plugin.rb | 46 +++ plugins/footnote/spec/pretty_text_spec.rb | 108 +++++ .../javascripts/acceptance/footnote-test.js | 57 +++ translator.yml | 7 + 59 files changed, 1256 insertions(+) create mode 100644 plugins/footnote/README.md create mode 100644 plugins/footnote/assets/javascripts/initializers/inline-footnotes.js create mode 100644 plugins/footnote/assets/javascripts/lib/discourse-markdown/footnotes.js create mode 100644 plugins/footnote/assets/stylesheets/footnotes.scss create mode 100644 plugins/footnote/assets/vendor/javascripts/markdown-it-footnote.js create mode 100644 plugins/footnote/config/locales/server.ar.yml create mode 100644 plugins/footnote/config/locales/server.be.yml create mode 100644 plugins/footnote/config/locales/server.bg.yml create mode 100644 plugins/footnote/config/locales/server.bs_BA.yml create mode 100644 plugins/footnote/config/locales/server.ca.yml create mode 100644 plugins/footnote/config/locales/server.cs.yml create mode 100644 plugins/footnote/config/locales/server.da.yml create mode 100644 plugins/footnote/config/locales/server.de.yml create mode 100644 plugins/footnote/config/locales/server.el.yml create mode 100644 plugins/footnote/config/locales/server.en.yml create mode 100644 plugins/footnote/config/locales/server.en_GB.yml create mode 100644 plugins/footnote/config/locales/server.es.yml create mode 100644 plugins/footnote/config/locales/server.et.yml create mode 100644 plugins/footnote/config/locales/server.fa_IR.yml create mode 100644 plugins/footnote/config/locales/server.fi.yml create mode 100644 plugins/footnote/config/locales/server.fr.yml create mode 100644 plugins/footnote/config/locales/server.gl.yml create mode 100644 plugins/footnote/config/locales/server.he.yml create mode 100644 plugins/footnote/config/locales/server.hr.yml create mode 100644 plugins/footnote/config/locales/server.hu.yml create mode 100644 plugins/footnote/config/locales/server.hy.yml create mode 100644 plugins/footnote/config/locales/server.id.yml create mode 100644 plugins/footnote/config/locales/server.it.yml create mode 100644 plugins/footnote/config/locales/server.ja.yml create mode 100644 plugins/footnote/config/locales/server.ko.yml create mode 100644 plugins/footnote/config/locales/server.lt.yml create mode 100644 plugins/footnote/config/locales/server.lv.yml create mode 100644 plugins/footnote/config/locales/server.nb_NO.yml create mode 100644 plugins/footnote/config/locales/server.nl.yml create mode 100644 plugins/footnote/config/locales/server.pl_PL.yml create mode 100644 plugins/footnote/config/locales/server.pt.yml create mode 100644 plugins/footnote/config/locales/server.pt_BR.yml create mode 100644 plugins/footnote/config/locales/server.ro.yml create mode 100644 plugins/footnote/config/locales/server.ru.yml create mode 100644 plugins/footnote/config/locales/server.sk.yml create mode 100644 plugins/footnote/config/locales/server.sl.yml create mode 100644 plugins/footnote/config/locales/server.sq.yml create mode 100644 plugins/footnote/config/locales/server.sr.yml create mode 100644 plugins/footnote/config/locales/server.sv.yml create mode 100644 plugins/footnote/config/locales/server.sw.yml create mode 100644 plugins/footnote/config/locales/server.te.yml create mode 100644 plugins/footnote/config/locales/server.th.yml create mode 100644 plugins/footnote/config/locales/server.tr_TR.yml create mode 100644 plugins/footnote/config/locales/server.uk.yml create mode 100644 plugins/footnote/config/locales/server.ur.yml create mode 100644 plugins/footnote/config/locales/server.vi.yml create mode 100644 plugins/footnote/config/locales/server.zh_CN.yml create mode 100644 plugins/footnote/config/locales/server.zh_TW.yml create mode 100644 plugins/footnote/config/settings.yml create mode 100644 plugins/footnote/plugin.rb create mode 100644 plugins/footnote/spec/pretty_text_spec.rb create mode 100644 plugins/footnote/test/javascripts/acceptance/footnote-test.js diff --git a/.gitignore b/.gitignore index 3c7b1482942..3b60900b38a 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ !/plugins/poll/ !/plugins/styleguide !/plugins/checklist/ +!/plugins/footnote/ /plugins/*/auto_generated/ /spec/fixtures/plugins/my_plugin/auto_generated diff --git a/plugins/footnote/README.md b/plugins/footnote/README.md new file mode 100644 index 00000000000..97982944e6e --- /dev/null +++ b/plugins/footnote/README.md @@ -0,0 +1,8 @@ +## Discourse footnotes plugin + +https://meta.discourse.org/t/discourse-footnote/84533 + +Official footnotes Discourse plugin + +Based off: [github.com/markdown-it/markdown-it-footnote](https://github.com/markdown-it/markdown-it-footnote) + diff --git a/plugins/footnote/assets/javascripts/initializers/inline-footnotes.js b/plugins/footnote/assets/javascripts/initializers/inline-footnotes.js new file mode 100644 index 00000000000..e910e1f4a83 --- /dev/null +++ b/plugins/footnote/assets/javascripts/initializers/inline-footnotes.js @@ -0,0 +1,130 @@ +import { createPopper } from "@popperjs/core"; +import { withPluginApi } from "discourse/lib/plugin-api"; +import { iconHTML } from "discourse-common/lib/icon-library"; + +let inlineFootnotePopper; + +function applyInlineFootnotes(elem) { + const footnoteRefs = elem.querySelectorAll("sup.footnote-ref"); + + footnoteRefs.forEach((footnoteRef) => { + const refLink = footnoteRef.querySelector("a"); + if (!refLink) { + return; + } + + const expandableFootnote = document.createElement("a"); + expandableFootnote.classList.add("expand-footnote"); + expandableFootnote.innerHTML = iconHTML("ellipsis-h"); + expandableFootnote.href = ""; + expandableFootnote.role = "button"; + expandableFootnote.dataset.footnoteId = refLink.getAttribute("href"); + + footnoteRef.after(expandableFootnote); + }); + + if (footnoteRefs.length) { + elem.classList.add("inline-footnotes"); + } +} + +function buildTooltip() { + let html = ` + + `; + + let template = document.createElement("template"); + html = html.trim(); + template.innerHTML = html; + return template.content.firstChild; +} + +function footNoteEventHandler(event) { + inlineFootnotePopper?.destroy(); + + const tooltip = document.getElementById("footnote-tooltip"); + + // reset state by hidding tooltip, it handles "click outside" + // allowing to hide the tooltip when you click anywhere else + tooltip?.removeAttribute("data-show"); + + // if we didn't actually click a footnote button, exit early + if (!event.target.classList.contains("expand-footnote")) { + return; + } + + event.preventDefault(); + event.stopPropagation(); + + // append footnote to tooltip body + const expandableFootnote = event.target; + const cooked = expandableFootnote.closest(".cooked"); + const footnoteId = expandableFootnote.dataset.footnoteId; + const footnoteContent = tooltip.querySelector(".footnote-tooltip-content"); + let newContent = cooked.querySelector(footnoteId); + + footnoteContent.innerHTML = newContent.innerHTML; + + // display tooltip + tooltip.dataset.show = ""; + + // setup popper + inlineFootnotePopper?.destroy(); + inlineFootnotePopper = createPopper(expandableFootnote, tooltip, { + modifiers: [ + { + name: "arrow", + options: { element: tooltip.querySelector("#arrow") }, + }, + { + name: "preventOverflow", + options: { + altAxis: true, + padding: 5, + }, + }, + { + name: "offset", + options: { + offset: [0, 12], + }, + }, + ], + }); +} + +export default { + name: "inline-footnotes", + + initialize(container) { + if (!container.lookup("site-settings:main").display_footnotes_inline) { + return; + } + + document.documentElement.append(buildTooltip()); + + window.addEventListener("click", footNoteEventHandler); + + withPluginApi("0.8.9", (api) => { + api.decorateCookedElement((elem) => applyInlineFootnotes(elem), { + onlyStream: true, + id: "inline-footnotes", + }); + + api.onPageChange(() => { + document + .getElementById("footnote-tooltip") + ?.removeAttribute("data-show"); + }); + }); + }, + + teardown() { + inlineFootnotePopper?.destroy(); + window.removeEventListener("click", footNoteEventHandler); + document.getElementById("footnote-tooltip")?.remove(); + }, +}; diff --git a/plugins/footnote/assets/javascripts/lib/discourse-markdown/footnotes.js b/plugins/footnote/assets/javascripts/lib/discourse-markdown/footnotes.js new file mode 100644 index 00000000000..6dce84d3d99 --- /dev/null +++ b/plugins/footnote/assets/javascripts/lib/discourse-markdown/footnotes.js @@ -0,0 +1,26 @@ +export function setup(helper) { + helper.registerOptions((opts, siteSettings) => { + opts.features["footnotes"] = + window.markdownitFootnote && !!siteSettings.enable_markdown_footnotes; + }); + + helper.allowList([ + "ol.footnotes-list", + "hr.footnotes-sep", + "li.footnote-item", + "a.footnote-backref", + "sup.footnote-ref", + ]); + + helper.allowList({ + custom(tag, name, value) { + if ((tag === "a" || tag === "li") && name === "id") { + return !!value.match(/^fn(ref)?\d+$/); + } + }, + }); + + if (window.markdownitFootnote) { + helper.registerPlugin(window.markdownitFootnote); + } +} diff --git a/plugins/footnote/assets/stylesheets/footnotes.scss b/plugins/footnote/assets/stylesheets/footnotes.scss new file mode 100644 index 00000000000..55476a8c1dc --- /dev/null +++ b/plugins/footnote/assets/stylesheets/footnotes.scss @@ -0,0 +1,106 @@ +.inline-footnotes { + a.expand-footnote { + user-select: none; + padding: 0px 0.5em; + margin: 0 0 0 0.25em; + color: var(--primary-low-mid-or-secondary-high); + background: var(--primary-low); + border-radius: 3px; + min-height: 20px; + display: inline-flex; + align-items: center; + + &:hover { + background: var(--primary-medium); + color: var(--secondary); + } + + > * { + pointer-events: none; + } + } + + // This is hack to work with lazy-loading, we will trick the browser + // to believe the image is in the DOM and can be loaded + .footnotes-list, + .footnotes-sep { + position: absolute; + // the left/right positioning prevents overflow issues + // with long words causing overflow on small screens + left: 0; + right: 0; + } + + .footnotes-sep, + .footnotes-list, + .footnote-ref { + display: none; + } +} + +#footnote-tooltip { + background-color: var(--primary-low); + color: var(--primary); + padding: 0.5em; + font-size: var(--font-down-1); + border-radius: 3px; + display: none; + z-index: z("tooltip"); + max-width: 400px; + overflow-wrap: break-word; + + .footnote-tooltip-content { + overflow: hidden; + + .footnote-backref { + display: none; + } + + img { + object-fit: cover; + max-width: 385px; + } + + p { + margin: 0; + } + } +} + +#footnote-tooltip[data-show] { + display: block; +} + +#arrow, +#arrow::before { + position: absolute; + width: 10px; + height: 10px; + background: inherit; +} + +#arrow { + visibility: hidden; +} + +#arrow::before { + visibility: visible; + content: ""; + transform: rotate(45deg); +} + +#footnote-tooltip[data-popper-placement^="top"] > #arrow { + bottom: -4px; +} + +#footnote-tooltip[data-popper-placement^="bottom"] > #arrow { + top: -4px; +} + +#footnote-tooltip[data-popper-placement^="left"] > #arrow { + right: -4px; +} + +#footnote-tooltip[data-popper-placement^="right"] > #arrow { + left: -4px; +} diff --git a/plugins/footnote/assets/vendor/javascripts/markdown-it-footnote.js b/plugins/footnote/assets/vendor/javascripts/markdown-it-footnote.js new file mode 100644 index 00000000000..7f5f9569cfa --- /dev/null +++ b/plugins/footnote/assets/vendor/javascripts/markdown-it-footnote.js @@ -0,0 +1,382 @@ +/* eslint-disable */ + +/*! markdown-it-footnote 3.0.3 https://github.com//markdown-it/markdown-it-footnote @license MIT */(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.markdownitFootnote = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i 0) { + n += ':' + tokens[idx].meta.subId; + } + + return '[' + n + ']'; +} + +function render_footnote_ref(tokens, idx, options, env, slf) { + var id = slf.rules.footnote_anchor_name(tokens, idx, options, env, slf); + var caption = slf.rules.footnote_caption(tokens, idx, options, env, slf); + var refid = id; + + if (tokens[idx].meta.subId > 0) { + refid += ':' + tokens[idx].meta.subId; + } + + return '' + caption + ''; +} + +function render_footnote_block_open(tokens, idx, options) { + return (options.xhtmlOut ? '
\n' : '
\n') + + '
\n' + + '
    \n'; +} + +function render_footnote_block_close() { + return '
\n
\n'; +} + +function render_footnote_open(tokens, idx, options, env, slf) { + var id = slf.rules.footnote_anchor_name(tokens, idx, options, env, slf); + + if (tokens[idx].meta.subId > 0) { + id += ':' + tokens[idx].meta.subId; + } + + return '
  • '; +} + +function render_footnote_close() { + return '
  • \n'; +} + +function render_footnote_anchor(tokens, idx, options, env, slf) { + var id = slf.rules.footnote_anchor_name(tokens, idx, options, env, slf); + + if (tokens[idx].meta.subId > 0) { + id += ':' + tokens[idx].meta.subId; + } + + /* ↩ with escape code to prevent display as Apple Emoji on iOS */ + return ' \u21a9\uFE0E'; +} + + +module.exports = function footnote_plugin(md) { + var parseLinkLabel = md.helpers.parseLinkLabel, + isSpace = md.utils.isSpace; + + md.renderer.rules.footnote_ref = render_footnote_ref; + md.renderer.rules.footnote_block_open = render_footnote_block_open; + md.renderer.rules.footnote_block_close = render_footnote_block_close; + md.renderer.rules.footnote_open = render_footnote_open; + md.renderer.rules.footnote_close = render_footnote_close; + md.renderer.rules.footnote_anchor = render_footnote_anchor; + + // helpers (only used in other rules, no tokens are attached to those) + md.renderer.rules.footnote_caption = render_footnote_caption; + md.renderer.rules.footnote_anchor_name = render_footnote_anchor_name; + + // Process footnote block definition + function footnote_def(state, startLine, endLine, silent) { + var oldBMark, oldTShift, oldSCount, oldParentType, pos, label, token, + initial, offset, ch, posAfterColon, + start = state.bMarks[startLine] + state.tShift[startLine], + max = state.eMarks[startLine]; + + // line should be at least 5 chars - "[^x]:" + if (start + 4 > max) { return false; } + + if (state.src.charCodeAt(start) !== 0x5B/* [ */) { return false; } + if (state.src.charCodeAt(start + 1) !== 0x5E/* ^ */) { return false; } + + for (pos = start + 2; pos < max; pos++) { + if (state.src.charCodeAt(pos) === 0x20) { return false; } + if (state.src.charCodeAt(pos) === 0x5D /* ] */) { + break; + } + } + + if (pos === start + 2) { return false; } // no empty footnote labels + if (pos + 1 >= max || state.src.charCodeAt(++pos) !== 0x3A /* : */) { return false; } + if (silent) { return true; } + pos++; + + if (!state.env.footnotes) { state.env.footnotes = {}; } + if (!state.env.footnotes.refs) { state.env.footnotes.refs = {}; } + label = state.src.slice(start + 2, pos - 2); + state.env.footnotes.refs[':' + label] = -1; + + token = new state.Token('footnote_reference_open', '', 1); + token.meta = { label: label }; + token.level = state.level++; + state.tokens.push(token); + + oldBMark = state.bMarks[startLine]; + oldTShift = state.tShift[startLine]; + oldSCount = state.sCount[startLine]; + oldParentType = state.parentType; + + posAfterColon = pos; + initial = offset = state.sCount[startLine] + pos - (state.bMarks[startLine] + state.tShift[startLine]); + + while (pos < max) { + ch = state.src.charCodeAt(pos); + + if (isSpace(ch)) { + if (ch === 0x09) { + offset += 4 - offset % 4; + } else { + offset++; + } + } else { + break; + } + + pos++; + } + + state.tShift[startLine] = pos - posAfterColon; + state.sCount[startLine] = offset - initial; + + state.bMarks[startLine] = posAfterColon; + state.blkIndent += 4; + state.parentType = 'footnote'; + + if (state.sCount[startLine] < state.blkIndent) { + state.sCount[startLine] += state.blkIndent; + } + + state.md.block.tokenize(state, startLine, endLine, true); + + state.parentType = oldParentType; + state.blkIndent -= 4; + state.tShift[startLine] = oldTShift; + state.sCount[startLine] = oldSCount; + state.bMarks[startLine] = oldBMark; + + token = new state.Token('footnote_reference_close', '', -1); + token.level = --state.level; + state.tokens.push(token); + + return true; + } + + // Process inline footnotes (^[...]) + function footnote_inline(state, silent) { + var labelStart, + labelEnd, + footnoteId, + token, + tokens, + max = state.posMax, + start = state.pos; + + if (start + 2 >= max) { return false; } + if (state.src.charCodeAt(start) !== 0x5E/* ^ */) { return false; } + if (state.src.charCodeAt(start + 1) !== 0x5B/* [ */) { return false; } + + labelStart = start + 2; + labelEnd = parseLinkLabel(state, start + 1); + + // parser failed to find ']', so it's not a valid note + if (labelEnd < 0) { return false; } + + // We found the end of the link, and know for a fact it's a valid link; + // so all that's left to do is to call tokenizer. + // + if (!silent) { + if (!state.env.footnotes) { state.env.footnotes = {}; } + if (!state.env.footnotes.list) { state.env.footnotes.list = []; } + footnoteId = state.env.footnotes.list.length; + + state.md.inline.parse( + state.src.slice(labelStart, labelEnd), + state.md, + state.env, + tokens = [] + ); + + // Having a rendered footnote inside a link creates a nested link, which + // is not valid HTML, so close the parent tag first before proceeding. + const previousToken = state.tokens[state.tokens.length - 1]; + if (previousToken && previousToken.content && previousToken.content.includes(" max) { return false; } + + if (!state.env.footnotes || !state.env.footnotes.refs) { return false; } + if (state.src.charCodeAt(start) !== 0x5B/* [ */) { return false; } + if (state.src.charCodeAt(start + 1) !== 0x5E/* ^ */) { return false; } + + for (pos = start + 2; pos < max; pos++) { + if (state.src.charCodeAt(pos) === 0x20) { return false; } + if (state.src.charCodeAt(pos) === 0x0A) { return false; } + if (state.src.charCodeAt(pos) === 0x5D /* ] */) { + break; + } + } + + if (pos === start + 2) { return false; } // no empty footnote labels + if (pos >= max) { return false; } + pos++; + + label = state.src.slice(start + 2, pos - 1); + if (typeof state.env.footnotes.refs[':' + label] === 'undefined') { return false; } + + if (!silent) { + if (!state.env.footnotes.list) { state.env.footnotes.list = []; } + + if (state.env.footnotes.refs[':' + label] < 0) { + footnoteId = state.env.footnotes.list.length; + state.env.footnotes.list[footnoteId] = { label: label, count: 0 }; + state.env.footnotes.refs[':' + label] = footnoteId; + } else { + footnoteId = state.env.footnotes.refs[':' + label]; + } + + footnoteSubId = state.env.footnotes.list[footnoteId].count; + state.env.footnotes.list[footnoteId].count++; + + token = state.push('footnote_ref', '', 0); + token.meta = { id: footnoteId, subId: footnoteSubId, label: label }; + } + + state.pos = pos; + state.posMax = max; + return true; + } + + // Glue footnote tokens to end of token stream + function footnote_tail(state) { + var i, l, j, t, lastParagraph, list, token, tokens, current, currentLabel, + insideRef = false, + refTokens = {}; + + if (!state.env.footnotes) { return; } + + state.tokens = state.tokens.filter(function (tok) { + if (tok.type === 'footnote_reference_open') { + insideRef = true; + current = []; + currentLabel = tok.meta.label; + return false; + } + if (tok.type === 'footnote_reference_close') { + insideRef = false; + // prepend ':' to avoid conflict with Object.prototype members + refTokens[':' + currentLabel] = current; + return false; + } + if (insideRef) { current.push(tok); } + return !insideRef; + }); + + if (!state.env.footnotes.list) { return; } + list = state.env.footnotes.list; + + token = new state.Token('footnote_block_open', '', 1); + state.tokens.push(token); + + for (i = 0, l = list.length; i < l; i++) { + token = new state.Token('footnote_open', '', 1); + token.meta = { id: i, label: list[i].label }; + state.tokens.push(token); + + if (list[i].tokens) { + tokens = []; + + token = new state.Token('paragraph_open', 'p', 1); + token.block = true; + tokens.push(token); + + token = new state.Token('inline', '', 0); + token.children = list[i].tokens; + token.content = list[i].content; + tokens.push(token); + + token = new state.Token('paragraph_close', 'p', -1); + token.block = true; + tokens.push(token); + + } else if (list[i].label) { + tokens = refTokens[':' + list[i].label]; + } + + if (tokens) state.tokens = state.tokens.concat(tokens); + if (state.tokens[state.tokens.length - 1].type === 'paragraph_close') { + lastParagraph = state.tokens.pop(); + } else { + lastParagraph = null; + } + + t = list[i].count > 0 ? list[i].count : 1; + for (j = 0; j < t; j++) { + token = new state.Token('footnote_anchor', '', 0); + token.meta = { id: i, subId: j, label: list[i].label }; + state.tokens.push(token); + } + + if (lastParagraph) { + state.tokens.push(lastParagraph); + } + + token = new state.Token('footnote_close', '', -1); + state.tokens.push(token); + } + + token = new state.Token('footnote_block_close', '', -1); + state.tokens.push(token); + } + + md.block.ruler.before('reference', 'footnote_def', footnote_def, { alt: [ 'paragraph', 'reference' ] }); + md.inline.ruler.after('image', 'footnote_inline', footnote_inline); + md.inline.ruler.after('footnote_inline', 'footnote_ref', footnote_ref); + md.core.ruler.after('inline', 'footnote_tail', footnote_tail); +}; + +},{}]},{},[1])(1) +}); diff --git a/plugins/footnote/config/locales/server.ar.yml b/plugins/footnote/config/locales/server.ar.yml new file mode 100644 index 00000000000..108b81d2930 --- /dev/null +++ b/plugins/footnote/config/locales/server.ar.yml @@ -0,0 +1,10 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +ar: + site_settings: + enable_markdown_footnotes: تفعيل تنسيق Markdown في الحاشية السفلية على هذا الموقع + display_footnotes_inline: تفعيل التوسيع المضمَّن للحواشي السفلية diff --git a/plugins/footnote/config/locales/server.be.yml b/plugins/footnote/config/locales/server.be.yml new file mode 100644 index 00000000000..2ea77a0d350 --- /dev/null +++ b/plugins/footnote/config/locales/server.be.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +be: diff --git a/plugins/footnote/config/locales/server.bg.yml b/plugins/footnote/config/locales/server.bg.yml new file mode 100644 index 00000000000..52333529d3c --- /dev/null +++ b/plugins/footnote/config/locales/server.bg.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +bg: diff --git a/plugins/footnote/config/locales/server.bs_BA.yml b/plugins/footnote/config/locales/server.bs_BA.yml new file mode 100644 index 00000000000..828a7e65af8 --- /dev/null +++ b/plugins/footnote/config/locales/server.bs_BA.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +bs_BA: diff --git a/plugins/footnote/config/locales/server.ca.yml b/plugins/footnote/config/locales/server.ca.yml new file mode 100644 index 00000000000..ec737bc1a5b --- /dev/null +++ b/plugins/footnote/config/locales/server.ca.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +ca: diff --git a/plugins/footnote/config/locales/server.cs.yml b/plugins/footnote/config/locales/server.cs.yml new file mode 100644 index 00000000000..041b2f0bd05 --- /dev/null +++ b/plugins/footnote/config/locales/server.cs.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +cs: diff --git a/plugins/footnote/config/locales/server.da.yml b/plugins/footnote/config/locales/server.da.yml new file mode 100644 index 00000000000..f39c727c594 --- /dev/null +++ b/plugins/footnote/config/locales/server.da.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +da: diff --git a/plugins/footnote/config/locales/server.de.yml b/plugins/footnote/config/locales/server.de.yml new file mode 100644 index 00000000000..98b21cfca2b --- /dev/null +++ b/plugins/footnote/config/locales/server.de.yml @@ -0,0 +1,10 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +de: + site_settings: + enable_markdown_footnotes: Fußnoten-Markdown auf dieser Website aktivieren + display_footnotes_inline: Inline-Erweiterung von Fußnoten aktivieren diff --git a/plugins/footnote/config/locales/server.el.yml b/plugins/footnote/config/locales/server.el.yml new file mode 100644 index 00000000000..d872d0ecc40 --- /dev/null +++ b/plugins/footnote/config/locales/server.el.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +el: diff --git a/plugins/footnote/config/locales/server.en.yml b/plugins/footnote/config/locales/server.en.yml new file mode 100644 index 00000000000..f671b78d867 --- /dev/null +++ b/plugins/footnote/config/locales/server.en.yml @@ -0,0 +1,4 @@ +en: + site_settings: + enable_markdown_footnotes: enable footnote markdown on this site + display_footnotes_inline: enable inline expansion of footnotes diff --git a/plugins/footnote/config/locales/server.en_GB.yml b/plugins/footnote/config/locales/server.en_GB.yml new file mode 100644 index 00000000000..2d4fa180ec7 --- /dev/null +++ b/plugins/footnote/config/locales/server.en_GB.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +en_GB: diff --git a/plugins/footnote/config/locales/server.es.yml b/plugins/footnote/config/locales/server.es.yml new file mode 100644 index 00000000000..a4b02da6d1f --- /dev/null +++ b/plugins/footnote/config/locales/server.es.yml @@ -0,0 +1,10 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +es: + site_settings: + enable_markdown_footnotes: habilitar el marcado de notas a pie de página en este sitio + display_footnotes_inline: habilitar la expansión en línea de notas al pie diff --git a/plugins/footnote/config/locales/server.et.yml b/plugins/footnote/config/locales/server.et.yml new file mode 100644 index 00000000000..0ea0b6d554b --- /dev/null +++ b/plugins/footnote/config/locales/server.et.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +et: diff --git a/plugins/footnote/config/locales/server.fa_IR.yml b/plugins/footnote/config/locales/server.fa_IR.yml new file mode 100644 index 00000000000..56512089fb5 --- /dev/null +++ b/plugins/footnote/config/locales/server.fa_IR.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +fa_IR: diff --git a/plugins/footnote/config/locales/server.fi.yml b/plugins/footnote/config/locales/server.fi.yml new file mode 100644 index 00000000000..0fe9460fcfd --- /dev/null +++ b/plugins/footnote/config/locales/server.fi.yml @@ -0,0 +1,10 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +fi: + site_settings: + enable_markdown_footnotes: ota käyttöön alaviitteiden merkintä tällä sivustolla + display_footnotes_inline: ota käyttöön alaviitteiden rivilaajennus diff --git a/plugins/footnote/config/locales/server.fr.yml b/plugins/footnote/config/locales/server.fr.yml new file mode 100644 index 00000000000..40728f96669 --- /dev/null +++ b/plugins/footnote/config/locales/server.fr.yml @@ -0,0 +1,10 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +fr: + site_settings: + enable_markdown_footnotes: activer le marquage de la note de bas de page sur ce site + display_footnotes_inline: activer le développement intégré des notes de bas de page diff --git a/plugins/footnote/config/locales/server.gl.yml b/plugins/footnote/config/locales/server.gl.yml new file mode 100644 index 00000000000..fb911ce1635 --- /dev/null +++ b/plugins/footnote/config/locales/server.gl.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +gl: diff --git a/plugins/footnote/config/locales/server.he.yml b/plugins/footnote/config/locales/server.he.yml new file mode 100644 index 00000000000..90c43f673f6 --- /dev/null +++ b/plugins/footnote/config/locales/server.he.yml @@ -0,0 +1,10 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +he: + site_settings: + enable_markdown_footnotes: הפעלת הערות שוליים ב־Markdown באתר הזה + display_footnotes_inline: הפעלת הרחבה בתוך השורה של הערות שוליים diff --git a/plugins/footnote/config/locales/server.hr.yml b/plugins/footnote/config/locales/server.hr.yml new file mode 100644 index 00000000000..93343ce6e19 --- /dev/null +++ b/plugins/footnote/config/locales/server.hr.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +hr: diff --git a/plugins/footnote/config/locales/server.hu.yml b/plugins/footnote/config/locales/server.hu.yml new file mode 100644 index 00000000000..76b6e9d8bcb --- /dev/null +++ b/plugins/footnote/config/locales/server.hu.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +hu: diff --git a/plugins/footnote/config/locales/server.hy.yml b/plugins/footnote/config/locales/server.hy.yml new file mode 100644 index 00000000000..cb18f64d356 --- /dev/null +++ b/plugins/footnote/config/locales/server.hy.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +hy: diff --git a/plugins/footnote/config/locales/server.id.yml b/plugins/footnote/config/locales/server.id.yml new file mode 100644 index 00000000000..596e36b2e13 --- /dev/null +++ b/plugins/footnote/config/locales/server.id.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +id: diff --git a/plugins/footnote/config/locales/server.it.yml b/plugins/footnote/config/locales/server.it.yml new file mode 100644 index 00000000000..797cf1dd9f7 --- /dev/null +++ b/plugins/footnote/config/locales/server.it.yml @@ -0,0 +1,10 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +it: + site_settings: + enable_markdown_footnotes: abilita markdown nelle note a piè di pagina su questo sito + display_footnotes_inline: consenti l'espansione inline delle note a piè di pagina diff --git a/plugins/footnote/config/locales/server.ja.yml b/plugins/footnote/config/locales/server.ja.yml new file mode 100644 index 00000000000..c1f966ee953 --- /dev/null +++ b/plugins/footnote/config/locales/server.ja.yml @@ -0,0 +1,10 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +ja: + site_settings: + enable_markdown_footnotes: このサイトで脚注マークダウンを有効にする + display_footnotes_inline: 脚注のインライン展開を有効にする diff --git a/plugins/footnote/config/locales/server.ko.yml b/plugins/footnote/config/locales/server.ko.yml new file mode 100644 index 00000000000..18dd77fd3ec --- /dev/null +++ b/plugins/footnote/config/locales/server.ko.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +ko: diff --git a/plugins/footnote/config/locales/server.lt.yml b/plugins/footnote/config/locales/server.lt.yml new file mode 100644 index 00000000000..16bb19758dc --- /dev/null +++ b/plugins/footnote/config/locales/server.lt.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +lt: diff --git a/plugins/footnote/config/locales/server.lv.yml b/plugins/footnote/config/locales/server.lv.yml new file mode 100644 index 00000000000..59e0ef6f4ed --- /dev/null +++ b/plugins/footnote/config/locales/server.lv.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +lv: diff --git a/plugins/footnote/config/locales/server.nb_NO.yml b/plugins/footnote/config/locales/server.nb_NO.yml new file mode 100644 index 00000000000..2e2224d1472 --- /dev/null +++ b/plugins/footnote/config/locales/server.nb_NO.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +nb_NO: diff --git a/plugins/footnote/config/locales/server.nl.yml b/plugins/footnote/config/locales/server.nl.yml new file mode 100644 index 00000000000..55d9f99cf41 --- /dev/null +++ b/plugins/footnote/config/locales/server.nl.yml @@ -0,0 +1,10 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +nl: + site_settings: + enable_markdown_footnotes: voetnootmarkdown inschakelen op deze site + display_footnotes_inline: inline uitbreiding van voetnoten inschakelen diff --git a/plugins/footnote/config/locales/server.pl_PL.yml b/plugins/footnote/config/locales/server.pl_PL.yml new file mode 100644 index 00000000000..6abcf488357 --- /dev/null +++ b/plugins/footnote/config/locales/server.pl_PL.yml @@ -0,0 +1,10 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +pl_PL: + site_settings: + enable_markdown_footnotes: włącz markdown dla przypisów na tej stronie + display_footnotes_inline: włącz wbudowane rozszerzenie przypisów diff --git a/plugins/footnote/config/locales/server.pt.yml b/plugins/footnote/config/locales/server.pt.yml new file mode 100644 index 00000000000..298ba523c1d --- /dev/null +++ b/plugins/footnote/config/locales/server.pt.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +pt: diff --git a/plugins/footnote/config/locales/server.pt_BR.yml b/plugins/footnote/config/locales/server.pt_BR.yml new file mode 100644 index 00000000000..128a8e33ad9 --- /dev/null +++ b/plugins/footnote/config/locales/server.pt_BR.yml @@ -0,0 +1,10 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +pt_BR: + site_settings: + enable_markdown_footnotes: ative redução de nota de rodapé neste site + display_footnotes_inline: ative expansão em linha das notas de rodapé diff --git a/plugins/footnote/config/locales/server.ro.yml b/plugins/footnote/config/locales/server.ro.yml new file mode 100644 index 00000000000..08a77f812ee --- /dev/null +++ b/plugins/footnote/config/locales/server.ro.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +ro: diff --git a/plugins/footnote/config/locales/server.ru.yml b/plugins/footnote/config/locales/server.ru.yml new file mode 100644 index 00000000000..182ccda977b --- /dev/null +++ b/plugins/footnote/config/locales/server.ru.yml @@ -0,0 +1,10 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +ru: + site_settings: + enable_markdown_footnotes: Включить Markdown для сносок на этом сайте + display_footnotes_inline: Включить встроенное расширение сносок diff --git a/plugins/footnote/config/locales/server.sk.yml b/plugins/footnote/config/locales/server.sk.yml new file mode 100644 index 00000000000..6f815624081 --- /dev/null +++ b/plugins/footnote/config/locales/server.sk.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +sk: diff --git a/plugins/footnote/config/locales/server.sl.yml b/plugins/footnote/config/locales/server.sl.yml new file mode 100644 index 00000000000..23489a48b1f --- /dev/null +++ b/plugins/footnote/config/locales/server.sl.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +sl: diff --git a/plugins/footnote/config/locales/server.sq.yml b/plugins/footnote/config/locales/server.sq.yml new file mode 100644 index 00000000000..7f051b7a7cf --- /dev/null +++ b/plugins/footnote/config/locales/server.sq.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +sq: diff --git a/plugins/footnote/config/locales/server.sr.yml b/plugins/footnote/config/locales/server.sr.yml new file mode 100644 index 00000000000..88d63d6ae1a --- /dev/null +++ b/plugins/footnote/config/locales/server.sr.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +sr: diff --git a/plugins/footnote/config/locales/server.sv.yml b/plugins/footnote/config/locales/server.sv.yml new file mode 100644 index 00000000000..c2fa3490619 --- /dev/null +++ b/plugins/footnote/config/locales/server.sv.yml @@ -0,0 +1,10 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +sv: + site_settings: + enable_markdown_footnotes: aktivera markdown för fotnot på denna webbplats + display_footnotes_inline: aktivera infogad expansion av fotnoter diff --git a/plugins/footnote/config/locales/server.sw.yml b/plugins/footnote/config/locales/server.sw.yml new file mode 100644 index 00000000000..0d7cdd075bf --- /dev/null +++ b/plugins/footnote/config/locales/server.sw.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +sw: diff --git a/plugins/footnote/config/locales/server.te.yml b/plugins/footnote/config/locales/server.te.yml new file mode 100644 index 00000000000..03967bdbb07 --- /dev/null +++ b/plugins/footnote/config/locales/server.te.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +te: diff --git a/plugins/footnote/config/locales/server.th.yml b/plugins/footnote/config/locales/server.th.yml new file mode 100644 index 00000000000..7de85ff91c4 --- /dev/null +++ b/plugins/footnote/config/locales/server.th.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +th: diff --git a/plugins/footnote/config/locales/server.tr_TR.yml b/plugins/footnote/config/locales/server.tr_TR.yml new file mode 100644 index 00000000000..557ef306b3f --- /dev/null +++ b/plugins/footnote/config/locales/server.tr_TR.yml @@ -0,0 +1,10 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +tr_TR: + site_settings: + enable_markdown_footnotes: bu sitede dipnot işaretlemeyi etkinleştirin + display_footnotes_inline: dipnotların satır içi genişletilmesini etkinleştirin diff --git a/plugins/footnote/config/locales/server.uk.yml b/plugins/footnote/config/locales/server.uk.yml new file mode 100644 index 00000000000..f1390545d1d --- /dev/null +++ b/plugins/footnote/config/locales/server.uk.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +uk: diff --git a/plugins/footnote/config/locales/server.ur.yml b/plugins/footnote/config/locales/server.ur.yml new file mode 100644 index 00000000000..b4a9c21ee2f --- /dev/null +++ b/plugins/footnote/config/locales/server.ur.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +ur: diff --git a/plugins/footnote/config/locales/server.vi.yml b/plugins/footnote/config/locales/server.vi.yml new file mode 100644 index 00000000000..f629dcf5329 --- /dev/null +++ b/plugins/footnote/config/locales/server.vi.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +vi: diff --git a/plugins/footnote/config/locales/server.zh_CN.yml b/plugins/footnote/config/locales/server.zh_CN.yml new file mode 100644 index 00000000000..468ab90650a --- /dev/null +++ b/plugins/footnote/config/locales/server.zh_CN.yml @@ -0,0 +1,10 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +zh_CN: + site_settings: + enable_markdown_footnotes: 在此站点上启用脚注 Markdown + display_footnotes_inline: 启用脚注内联扩展 diff --git a/plugins/footnote/config/locales/server.zh_TW.yml b/plugins/footnote/config/locales/server.zh_TW.yml new file mode 100644 index 00000000000..7e15fab0018 --- /dev/null +++ b/plugins/footnote/config/locales/server.zh_TW.yml @@ -0,0 +1,7 @@ +# WARNING: Never edit this file. +# It will be overwritten when translations are pulled from Crowdin. +# +# To work with us on translations, join this project: +# https://translate.discourse.org/ + +zh_TW: diff --git a/plugins/footnote/config/settings.yml b/plugins/footnote/config/settings.yml new file mode 100644 index 00000000000..45af30da580 --- /dev/null +++ b/plugins/footnote/config/settings.yml @@ -0,0 +1,7 @@ +plugins: + enable_markdown_footnotes: + default: true + client: true + display_footnotes_inline: + default: true + client: true diff --git a/plugins/footnote/plugin.rb b/plugins/footnote/plugin.rb new file mode 100644 index 00000000000..89fc270745f --- /dev/null +++ b/plugins/footnote/plugin.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +# name: footnote +# about: Adds markdown.it footnote support to Discourse +# version: 1.0 +# authors: Discourse Team +# url: https://github.com/discourse/discourse/tree/main/plugins/footnote + +enabled_site_setting :enable_markdown_footnotes + +register_asset "vendor/javascripts/markdown-it-footnote.js", :vendored_pretty_text + +register_asset "stylesheets/footnotes.scss" + +register_svg_icon "ellipsis-h" if respond_to?(:register_svg_icon) + +on(:before_post_process_cooked) do |doc, post| + doc + .css("a.footnote-backref") + .each do |backref| + href = backref["href"] || "" + id = href[6..-1].to_i + backref["href"] = "#footnote-ref-#{post.id}-#{id}" + end + + doc + .css("sup.footnote-ref a") + .each do |ref| + href = ref["href"] || "" + id = href[3..-1].to_i + ref["href"] = "#footnote-#{post.id}-#{id}" + + id = ref["id"] || "" + id = id[5..-1].to_i + ref["id"] = "footnote-ref-#{post.id}-#{id}" + end + + doc + .css("li.footnote-item") + .each do |li| + id = li["id"] || "" + id = id[2..-1].to_i + + li["id"] = "footnote-#{post.id}-#{id}" + end +end diff --git a/plugins/footnote/spec/pretty_text_spec.rb b/plugins/footnote/spec/pretty_text_spec.rb new file mode 100644 index 00000000000..def57a98c44 --- /dev/null +++ b/plugins/footnote/spec/pretty_text_spec.rb @@ -0,0 +1,108 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe PrettyText do + before { SiteSetting.queue_jobs = false } + + it "can be disabled" do + SiteSetting.enable_markdown_footnotes = false + + markdown = <<~MD + Here is a footnote, [^1] + + [^1]: I am one + MD + + html = <<~HTML +

    Here is a footnote, [^1]

    \n

    [^1]: I am one

    + HTML + + cooked = PrettyText.cook markdown.strip + expect(cooked).to eq(html.strip) + end + + it "supports normal footnotes" do + markdown = <<~MD + Here is a footnote, [^1] and another. [^test] + + [^1]: I am one + + [^test]: I am one + + test multiline + MD + + html = <<~HTML +

    Here is a footnote, [1] and another. [2]

    +

    test multiline

    +
    + +
      +
    1. I am one ↩︎

      +
    2. +
    3. I am one ↩︎

      +
    4. +
    + HTML + + cooked = PrettyText.cook markdown.strip + expect(cooked).to eq(html.strip) + end + + it "applies unique ids to elements after cooking a post" do + raw = <<~MD + Here is a footnote, [^1] and another. [^test] + + [^1]: I am one + + [^test]: I am one + + test multiline + MD + + post = create_post(raw: raw) + post.reload + + html = <<~HTML +

    Here is a footnote, [1] and another. [2]

    +

    test multiline

    +
    + +
      +
    1. I am one ↩︎

      +
    2. +
    3. I am one ↩︎

      +
    4. +
    + HTML + + expect(post.cooked.strip).to eq(html.strip) + end + + it "supports inline footnotes wrapped in elements by ending the elements early" do + raw = <<~MD + I have a point, see footnote. ^[the point] + + ^[footnote] + MD + + post = create_post(raw: raw) + post.reload + + html = <<~HTML +

    I have a point, see footnote. [1]

    +

    [2]

    +
    + +
      +
    1. the point ↩︎

      +
    2. +
    3. footnote ↩︎

      +
    4. +
    + HTML + + expect(post.cooked.strip).to eq(html.strip) + end +end diff --git a/plugins/footnote/test/javascripts/acceptance/footnote-test.js b/plugins/footnote/test/javascripts/acceptance/footnote-test.js new file mode 100644 index 00000000000..4ff4d8f6603 --- /dev/null +++ b/plugins/footnote/test/javascripts/acceptance/footnote-test.js @@ -0,0 +1,57 @@ +import { click, visit } from "@ember/test-helpers"; +import { test } from "qunit"; +import topicFixtures from "discourse/tests/fixtures/topic"; +import { acceptance, exists } from "discourse/tests/helpers/qunit-helpers"; +import { cloneJSON } from "discourse-common/lib/object"; + +acceptance("Discourse Foonote Plugin", function (needs) { + needs.user(); + + needs.settings({ + display_footnotes_inline: true, + }); + + needs.pretender((server, helper) => { + server.get("/t/45.json", () => { + let topic = cloneJSON(topicFixtures["/t/28830/1.json"]); + topic["post_stream"]["posts"][0]["cooked"] = ` +

    Lorem ipsum dolor sit amet[1]

    +

    Second reference should also work. [1]

    +
    +
      +
    1. +

      consectetur adipiscing elit ↩︎

      +
    2. +
    + `; + return helper.response(topic); + }); + }); + + test("displays the foonote on click", async function (assert) { + await visit("/t/45"); + + const tooltip = document.getElementById("footnote-tooltip"); + assert.ok(exists(tooltip)); + + await click(".expand-footnote"); + assert.equal( + tooltip.querySelector(".footnote-tooltip-content").textContent.trim(), + "consectetur adipiscing elit ↩︎" + ); + }); + + test("clicking a second footnote with same name works", async function (assert) { + await visit("/t/45"); + + const tooltip = document.getElementById("footnote-tooltip"); + assert.ok(exists(tooltip)); + + await click(".second .expand-footnote"); + + assert.equal( + tooltip.querySelector(".footnote-tooltip-content").textContent.trim(), + "consectetur adipiscing elit ↩︎" + ); + }); +}); diff --git a/translator.yml b/translator.yml index cfc3387d660..a8215bd30a0 100644 --- a/translator.yml +++ b/translator.yml @@ -79,3 +79,10 @@ files: - source_path: plugins/checklist/config/locales/server.en.yml destination_path: plugins/checklist/server.yml label: checklist + + - source_path: plugins/footnote/config/locales/client.en.yml + destination_path: plugins/footnote/client.yml + label: footnote + - source_path: plugins/footnote/config/locales/server.en.yml + destination_path: plugins/footnote/server.yml + label: footnote