mirror of
https://github.com/discourse/discourse.git
synced 2025-05-24 03:36:18 +08:00
FEATURE: Bundle discourse-footnote plugin into core (#23995)
Formerly https://github.com/discourse/discourse-footnote
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -43,6 +43,7 @@
|
|||||||
!/plugins/poll/
|
!/plugins/poll/
|
||||||
!/plugins/styleguide
|
!/plugins/styleguide
|
||||||
!/plugins/checklist/
|
!/plugins/checklist/
|
||||||
|
!/plugins/footnote/
|
||||||
/plugins/*/auto_generated/
|
/plugins/*/auto_generated/
|
||||||
|
|
||||||
/spec/fixtures/plugins/my_plugin/auto_generated
|
/spec/fixtures/plugins/my_plugin/auto_generated
|
||||||
|
8
plugins/footnote/README.md
Normal file
8
plugins/footnote/README.md
Normal file
@ -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)
|
||||||
|
|
@ -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 = `
|
||||||
|
<div id="footnote-tooltip" role="tooltip">
|
||||||
|
<div class="footnote-tooltip-content"></div>
|
||||||
|
<div id="arrow" data-popper-arrow></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
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();
|
||||||
|
},
|
||||||
|
};
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
106
plugins/footnote/assets/stylesheets/footnotes.scss
Normal file
106
plugins/footnote/assets/stylesheets/footnotes.scss
Normal file
@ -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;
|
||||||
|
}
|
382
plugins/footnote/assets/vendor/javascripts/markdown-it-footnote.js
vendored
Normal file
382
plugins/footnote/assets/vendor/javascripts/markdown-it-footnote.js
vendored
Normal file
@ -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<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
|
||||||
|
// Process footnotes
|
||||||
|
//
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Renderer partials
|
||||||
|
|
||||||
|
function render_footnote_anchor_name(tokens, idx, options, env/*, slf*/) {
|
||||||
|
var n = Number(tokens[idx].meta.id + 1).toString();
|
||||||
|
var prefix = '';
|
||||||
|
|
||||||
|
if (typeof env.docId === 'string') {
|
||||||
|
prefix = '-' + env.docId + '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
return prefix + n;
|
||||||
|
}
|
||||||
|
|
||||||
|
function render_footnote_caption(tokens, idx/*, options, env, slf*/) {
|
||||||
|
var n = Number(tokens[idx].meta.id + 1).toString();
|
||||||
|
|
||||||
|
if (tokens[idx].meta.subId > 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 '<sup class="footnote-ref"><a href="#fn' + id + '" id="fnref' + refid + '">' + caption + '</a></sup>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function render_footnote_block_open(tokens, idx, options) {
|
||||||
|
return (options.xhtmlOut ? '<hr class="footnotes-sep" />\n' : '<hr class="footnotes-sep">\n') +
|
||||||
|
'<section class="footnotes">\n' +
|
||||||
|
'<ol class="footnotes-list">\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
function render_footnote_block_close() {
|
||||||
|
return '</ol>\n</section>\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 '<li id="fn' + id + '" class="footnote-item">';
|
||||||
|
}
|
||||||
|
|
||||||
|
function render_footnote_close() {
|
||||||
|
return '</li>\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 ' <a href="#fnref' + id + '" class="footnote-backref">\u21a9\uFE0E</a>';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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("<a")) {
|
||||||
|
const linkCloseToken = state.push("html_inline", "", 0);
|
||||||
|
linkCloseToken.content = "</a>";
|
||||||
|
linkCloseToken.block = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
token = state.push('footnote_ref', '', 0);
|
||||||
|
token.meta = { id: footnoteId };
|
||||||
|
|
||||||
|
state.env.footnotes.list[footnoteId] = {
|
||||||
|
content: state.src.slice(labelStart, labelEnd),
|
||||||
|
tokens: tokens
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
state.pos = labelEnd + 1;
|
||||||
|
state.posMax = max;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process footnote references ([^...])
|
||||||
|
function footnote_ref(state, silent) {
|
||||||
|
var label,
|
||||||
|
pos,
|
||||||
|
footnoteId,
|
||||||
|
footnoteSubId,
|
||||||
|
token,
|
||||||
|
max = state.posMax,
|
||||||
|
start = state.pos;
|
||||||
|
|
||||||
|
// should be at least 4 chars - "[^x]"
|
||||||
|
if (start + 3 > 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)
|
||||||
|
});
|
10
plugins/footnote/config/locales/server.ar.yml
Normal file
10
plugins/footnote/config/locales/server.ar.yml
Normal file
@ -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: تفعيل التوسيع المضمَّن للحواشي السفلية
|
7
plugins/footnote/config/locales/server.be.yml
Normal file
7
plugins/footnote/config/locales/server.be.yml
Normal file
@ -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:
|
7
plugins/footnote/config/locales/server.bg.yml
Normal file
7
plugins/footnote/config/locales/server.bg.yml
Normal file
@ -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:
|
7
plugins/footnote/config/locales/server.bs_BA.yml
Normal file
7
plugins/footnote/config/locales/server.bs_BA.yml
Normal file
@ -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:
|
7
plugins/footnote/config/locales/server.ca.yml
Normal file
7
plugins/footnote/config/locales/server.ca.yml
Normal file
@ -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:
|
7
plugins/footnote/config/locales/server.cs.yml
Normal file
7
plugins/footnote/config/locales/server.cs.yml
Normal file
@ -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:
|
7
plugins/footnote/config/locales/server.da.yml
Normal file
7
plugins/footnote/config/locales/server.da.yml
Normal file
@ -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:
|
10
plugins/footnote/config/locales/server.de.yml
Normal file
10
plugins/footnote/config/locales/server.de.yml
Normal file
@ -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
|
7
plugins/footnote/config/locales/server.el.yml
Normal file
7
plugins/footnote/config/locales/server.el.yml
Normal file
@ -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:
|
4
plugins/footnote/config/locales/server.en.yml
Normal file
4
plugins/footnote/config/locales/server.en.yml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
en:
|
||||||
|
site_settings:
|
||||||
|
enable_markdown_footnotes: enable footnote markdown on this site
|
||||||
|
display_footnotes_inline: enable inline expansion of footnotes
|
7
plugins/footnote/config/locales/server.en_GB.yml
Normal file
7
plugins/footnote/config/locales/server.en_GB.yml
Normal file
@ -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:
|
10
plugins/footnote/config/locales/server.es.yml
Normal file
10
plugins/footnote/config/locales/server.es.yml
Normal file
@ -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
|
7
plugins/footnote/config/locales/server.et.yml
Normal file
7
plugins/footnote/config/locales/server.et.yml
Normal file
@ -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:
|
7
plugins/footnote/config/locales/server.fa_IR.yml
Normal file
7
plugins/footnote/config/locales/server.fa_IR.yml
Normal file
@ -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:
|
10
plugins/footnote/config/locales/server.fi.yml
Normal file
10
plugins/footnote/config/locales/server.fi.yml
Normal file
@ -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
|
10
plugins/footnote/config/locales/server.fr.yml
Normal file
10
plugins/footnote/config/locales/server.fr.yml
Normal file
@ -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
|
7
plugins/footnote/config/locales/server.gl.yml
Normal file
7
plugins/footnote/config/locales/server.gl.yml
Normal file
@ -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:
|
10
plugins/footnote/config/locales/server.he.yml
Normal file
10
plugins/footnote/config/locales/server.he.yml
Normal file
@ -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: הפעלת הרחבה בתוך השורה של הערות שוליים
|
7
plugins/footnote/config/locales/server.hr.yml
Normal file
7
plugins/footnote/config/locales/server.hr.yml
Normal file
@ -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:
|
7
plugins/footnote/config/locales/server.hu.yml
Normal file
7
plugins/footnote/config/locales/server.hu.yml
Normal file
@ -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:
|
7
plugins/footnote/config/locales/server.hy.yml
Normal file
7
plugins/footnote/config/locales/server.hy.yml
Normal file
@ -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:
|
7
plugins/footnote/config/locales/server.id.yml
Normal file
7
plugins/footnote/config/locales/server.id.yml
Normal file
@ -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:
|
10
plugins/footnote/config/locales/server.it.yml
Normal file
10
plugins/footnote/config/locales/server.it.yml
Normal file
@ -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
|
10
plugins/footnote/config/locales/server.ja.yml
Normal file
10
plugins/footnote/config/locales/server.ja.yml
Normal file
@ -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: 脚注のインライン展開を有効にする
|
7
plugins/footnote/config/locales/server.ko.yml
Normal file
7
plugins/footnote/config/locales/server.ko.yml
Normal file
@ -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:
|
7
plugins/footnote/config/locales/server.lt.yml
Normal file
7
plugins/footnote/config/locales/server.lt.yml
Normal file
@ -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:
|
7
plugins/footnote/config/locales/server.lv.yml
Normal file
7
plugins/footnote/config/locales/server.lv.yml
Normal file
@ -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:
|
7
plugins/footnote/config/locales/server.nb_NO.yml
Normal file
7
plugins/footnote/config/locales/server.nb_NO.yml
Normal file
@ -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:
|
10
plugins/footnote/config/locales/server.nl.yml
Normal file
10
plugins/footnote/config/locales/server.nl.yml
Normal file
@ -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
|
10
plugins/footnote/config/locales/server.pl_PL.yml
Normal file
10
plugins/footnote/config/locales/server.pl_PL.yml
Normal file
@ -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
|
7
plugins/footnote/config/locales/server.pt.yml
Normal file
7
plugins/footnote/config/locales/server.pt.yml
Normal file
@ -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:
|
10
plugins/footnote/config/locales/server.pt_BR.yml
Normal file
10
plugins/footnote/config/locales/server.pt_BR.yml
Normal file
@ -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é
|
7
plugins/footnote/config/locales/server.ro.yml
Normal file
7
plugins/footnote/config/locales/server.ro.yml
Normal file
@ -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:
|
10
plugins/footnote/config/locales/server.ru.yml
Normal file
10
plugins/footnote/config/locales/server.ru.yml
Normal file
@ -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: Включить встроенное расширение сносок
|
7
plugins/footnote/config/locales/server.sk.yml
Normal file
7
plugins/footnote/config/locales/server.sk.yml
Normal file
@ -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:
|
7
plugins/footnote/config/locales/server.sl.yml
Normal file
7
plugins/footnote/config/locales/server.sl.yml
Normal file
@ -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:
|
7
plugins/footnote/config/locales/server.sq.yml
Normal file
7
plugins/footnote/config/locales/server.sq.yml
Normal file
@ -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:
|
7
plugins/footnote/config/locales/server.sr.yml
Normal file
7
plugins/footnote/config/locales/server.sr.yml
Normal file
@ -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:
|
10
plugins/footnote/config/locales/server.sv.yml
Normal file
10
plugins/footnote/config/locales/server.sv.yml
Normal file
@ -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
|
7
plugins/footnote/config/locales/server.sw.yml
Normal file
7
plugins/footnote/config/locales/server.sw.yml
Normal file
@ -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:
|
7
plugins/footnote/config/locales/server.te.yml
Normal file
7
plugins/footnote/config/locales/server.te.yml
Normal file
@ -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:
|
7
plugins/footnote/config/locales/server.th.yml
Normal file
7
plugins/footnote/config/locales/server.th.yml
Normal file
@ -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:
|
10
plugins/footnote/config/locales/server.tr_TR.yml
Normal file
10
plugins/footnote/config/locales/server.tr_TR.yml
Normal file
@ -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
|
7
plugins/footnote/config/locales/server.uk.yml
Normal file
7
plugins/footnote/config/locales/server.uk.yml
Normal file
@ -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:
|
7
plugins/footnote/config/locales/server.ur.yml
Normal file
7
plugins/footnote/config/locales/server.ur.yml
Normal file
@ -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:
|
7
plugins/footnote/config/locales/server.vi.yml
Normal file
7
plugins/footnote/config/locales/server.vi.yml
Normal file
@ -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:
|
10
plugins/footnote/config/locales/server.zh_CN.yml
Normal file
10
plugins/footnote/config/locales/server.zh_CN.yml
Normal file
@ -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: 启用脚注内联扩展
|
7
plugins/footnote/config/locales/server.zh_TW.yml
Normal file
7
plugins/footnote/config/locales/server.zh_TW.yml
Normal file
@ -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:
|
7
plugins/footnote/config/settings.yml
Normal file
7
plugins/footnote/config/settings.yml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
plugins:
|
||||||
|
enable_markdown_footnotes:
|
||||||
|
default: true
|
||||||
|
client: true
|
||||||
|
display_footnotes_inline:
|
||||||
|
default: true
|
||||||
|
client: true
|
46
plugins/footnote/plugin.rb
Normal file
46
plugins/footnote/plugin.rb
Normal file
@ -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
|
108
plugins/footnote/spec/pretty_text_spec.rb
Normal file
108
plugins/footnote/spec/pretty_text_spec.rb
Normal file
@ -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
|
||||||
|
<p>Here is a footnote, [^1]</p>\n<p>[^1]: I am one</p>
|
||||||
|
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
|
||||||
|
<p>Here is a footnote, <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> and another. <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup></p>
|
||||||
|
<p>test multiline</p>
|
||||||
|
<hr class="footnotes-sep">
|
||||||
|
|
||||||
|
<ol class="footnotes-list">
|
||||||
|
<li id="fn1" class="footnote-item"><p>I am one <a href="#fnref1" class="footnote-backref">↩︎</a></p>
|
||||||
|
</li>
|
||||||
|
<li id="fn2" class="footnote-item"><p>I am one <a href="#fnref2" class="footnote-backref">↩︎</a></p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
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
|
||||||
|
<p>Here is a footnote, <sup class="footnote-ref"><a href="#footnote-#{post.id}-1" id="footnote-ref-#{post.id}-1">[1]</a></sup> and another. <sup class="footnote-ref"><a href="#footnote-#{post.id}-2" id="footnote-ref-#{post.id}-2">[2]</a></sup></p>
|
||||||
|
<p>test multiline</p>
|
||||||
|
<hr class="footnotes-sep">
|
||||||
|
|
||||||
|
<ol class="footnotes-list">
|
||||||
|
<li id="footnote-#{post.id}-1" class="footnote-item"><p>I am one <a href="#footnote-ref-#{post.id}-1" class="footnote-backref">↩︎</a></p>
|
||||||
|
</li>
|
||||||
|
<li id="footnote-#{post.id}-2" class="footnote-item"><p>I am one <a href="#footnote-ref-#{post.id}-2" class="footnote-backref">↩︎</a></p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
HTML
|
||||||
|
|
||||||
|
expect(post.cooked.strip).to eq(html.strip)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "supports inline footnotes wrapped in <a> elements by ending the elements early" do
|
||||||
|
raw = <<~MD
|
||||||
|
I have a point, see footnote. <a>^[the point]</a>
|
||||||
|
|
||||||
|
<a>^[footnote]</a>
|
||||||
|
MD
|
||||||
|
|
||||||
|
post = create_post(raw: raw)
|
||||||
|
post.reload
|
||||||
|
|
||||||
|
html = <<~HTML
|
||||||
|
<p>I have a point, see footnote. <a></a><sup class="footnote-ref"><a href="#footnote-#{post.id}-1" id="footnote-ref-#{post.id}-1">[1]</a></sup></p>
|
||||||
|
<p><a></a><sup class="footnote-ref"><a href="#footnote-#{post.id}-2" id="footnote-ref-#{post.id}-2">[2]</a></sup></p>
|
||||||
|
<hr class="footnotes-sep">
|
||||||
|
|
||||||
|
<ol class="footnotes-list">
|
||||||
|
<li id="footnote-#{post.id}-1" class="footnote-item"><p>the point <a href="#footnote-ref-#{post.id}-1" class="footnote-backref">↩︎</a></p>
|
||||||
|
</li>
|
||||||
|
<li id="footnote-#{post.id}-2" class="footnote-item"><p>footnote <a href="#footnote-ref-#{post.id}-2" class="footnote-backref">↩︎</a></p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
HTML
|
||||||
|
|
||||||
|
expect(post.cooked.strip).to eq(html.strip)
|
||||||
|
end
|
||||||
|
end
|
@ -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"] = `
|
||||||
|
<p>Lorem ipsum dolor sit amet<sup class="footnote-ref"><a href="#footnote-17-1" id="footnote-ref-17-1">[1]</a></sup></p>
|
||||||
|
<p class="second">Second reference should also work. <sup class="footnote-ref"><a href="#footnote-17-1" id="footnote-ref-17-0">[1]</a></sup></p>
|
||||||
|
<hr class="footnotes-sep">
|
||||||
|
<ol class="footnotes-list">
|
||||||
|
<li id="footnote-17-1" class="footnote-item">
|
||||||
|
<p>consectetur adipiscing elit <a href="#footnote-ref-17-1" class="footnote-backref">↩︎</a></p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
`;
|
||||||
|
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 ↩︎"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
@ -79,3 +79,10 @@ files:
|
|||||||
- source_path: plugins/checklist/config/locales/server.en.yml
|
- source_path: plugins/checklist/config/locales/server.en.yml
|
||||||
destination_path: plugins/checklist/server.yml
|
destination_path: plugins/checklist/server.yml
|
||||||
label: checklist
|
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
|
||||||
|
Reference in New Issue
Block a user