Files
dealplustech/node_modules/micromark-extension-gfm-footnote/lib/html.js
Kunthawat 77ac4d2d05 feat: Upgrade to Astro with full PDPA compliance
PDPA Features:
 Cookie consent banner
 Consent logging API
 Admin dashboard
 Privacy Policy
 Terms & Conditions

Technical:
 Astro 5.x + Tailwind v4
 Docker on port 80
 SQLite database
 15 pages built

Ready for Easypanel deployment.
2026-03-12 10:01:04 +07:00

154 lines
6.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @import {HtmlOptions as Options} from 'micromark-extension-gfm-footnote'
* @import {HtmlExtension} from 'micromark-util-types'
*/
import { normalizeIdentifier } from 'micromark-util-normalize-identifier';
import { sanitizeUri } from 'micromark-util-sanitize-uri';
const own = {}.hasOwnProperty;
/** @type {Options} */
const emptyOptions = {};
/**
* Generate the default label that GitHub uses on backreferences.
*
* @param {number} referenceIndex
* Index of the definition in the order that they are first referenced,
* 0-indexed.
* @param {number} rereferenceIndex
* Index of calls to the same definition, 0-indexed.
* @returns {string}
* Default label.
*/
export function defaultBackLabel(referenceIndex, rereferenceIndex) {
return 'Back to reference ' + (referenceIndex + 1) + (rereferenceIndex > 1 ? '-' + rereferenceIndex : '');
}
/**
* Create an extension for `micromark` to support GFM footnotes when
* serializing to HTML.
*
* @param {Options | null | undefined} [options={}]
* Configuration (optional).
* @returns {HtmlExtension}
* Extension for `micromark` that can be passed in `htmlExtensions` to
* support GFM footnotes when serializing to HTML.
*/
export function gfmFootnoteHtml(options) {
const config = options || emptyOptions;
const label = config.label || 'Footnotes';
const labelTagName = config.labelTagName || 'h2';
const labelAttributes = config.labelAttributes === null || config.labelAttributes === undefined ? 'class="sr-only"' : config.labelAttributes;
const backLabel = config.backLabel || defaultBackLabel;
const clobberPrefix = config.clobberPrefix === null || config.clobberPrefix === undefined ? 'user-content-' : config.clobberPrefix;
return {
enter: {
gfmFootnoteDefinition() {
const stack = this.getData('tightStack');
stack.push(false);
},
gfmFootnoteDefinitionLabelString() {
this.buffer();
},
gfmFootnoteCallString() {
this.buffer();
}
},
exit: {
gfmFootnoteDefinition() {
let definitions = this.getData('gfmFootnoteDefinitions');
const footnoteStack = this.getData('gfmFootnoteDefinitionStack');
const tightStack = this.getData('tightStack');
const current = footnoteStack.pop();
const value = this.resume();
if (!definitions) {
this.setData('gfmFootnoteDefinitions', definitions = {});
}
if (!own.call(definitions, current)) definitions[current] = value;
tightStack.pop();
this.setData('slurpOneLineEnding', true);
// “Hack” to prevent a line ending from showing up if were in a definition in
// an empty list item.
this.setData('lastWasTag');
},
gfmFootnoteDefinitionLabelString(token) {
let footnoteStack = this.getData('gfmFootnoteDefinitionStack');
if (!footnoteStack) {
this.setData('gfmFootnoteDefinitionStack', footnoteStack = []);
}
footnoteStack.push(normalizeIdentifier(this.sliceSerialize(token)));
this.resume(); // Drop the label.
this.buffer(); // Get ready for a value.
},
gfmFootnoteCallString(token) {
let calls = this.getData('gfmFootnoteCallOrder');
let counts = this.getData('gfmFootnoteCallCounts');
const id = normalizeIdentifier(this.sliceSerialize(token));
/** @type {number} */
let counter;
this.resume();
if (!calls) this.setData('gfmFootnoteCallOrder', calls = []);
if (!counts) this.setData('gfmFootnoteCallCounts', counts = {});
const index = calls.indexOf(id);
const safeId = sanitizeUri(id.toLowerCase());
if (index === -1) {
calls.push(id);
counts[id] = 1;
counter = calls.length;
} else {
counts[id]++;
counter = index + 1;
}
const reuseCounter = counts[id];
this.tag('<sup><a href="#' + clobberPrefix + 'fn-' + safeId + '" id="' + clobberPrefix + 'fnref-' + safeId + (reuseCounter > 1 ? '-' + reuseCounter : '') + '" data-footnote-ref="" aria-describedby="footnote-label">' + String(counter) + '</a></sup>');
},
null() {
const calls = this.getData('gfmFootnoteCallOrder') || [];
const counts = this.getData('gfmFootnoteCallCounts') || {};
const definitions = this.getData('gfmFootnoteDefinitions') || {};
let index = -1;
if (calls.length > 0) {
this.lineEndingIfNeeded();
this.tag('<section data-footnotes="" class="footnotes"><' + labelTagName + ' id="footnote-label"' + (labelAttributes ? ' ' + labelAttributes : '') + '>');
this.raw(this.encode(label));
this.tag('</' + labelTagName + '>');
this.lineEndingIfNeeded();
this.tag('<ol>');
}
while (++index < calls.length) {
// Called definitions are always defined.
const id = calls[index];
const safeId = sanitizeUri(id.toLowerCase());
let referenceIndex = 0;
/** @type {Array<string>} */
const references = [];
while (++referenceIndex <= counts[id]) {
references.push('<a href="#' + clobberPrefix + 'fnref-' + safeId + (referenceIndex > 1 ? '-' + referenceIndex : '') + '" data-footnote-backref="" aria-label="' + this.encode(typeof backLabel === 'string' ? backLabel : backLabel(index, referenceIndex)) + '" class="data-footnote-backref">↩' + (referenceIndex > 1 ? '<sup>' + referenceIndex + '</sup>' : '') + '</a>');
}
const reference = references.join(' ');
let injected = false;
this.lineEndingIfNeeded();
this.tag('<li id="' + clobberPrefix + 'fn-' + safeId + '">');
this.lineEndingIfNeeded();
this.tag(definitions[id].replace(/<\/p>(?:\r?\n|\r)?$/, function ($0) {
injected = true;
return ' ' + reference + $0;
}));
if (!injected) {
this.lineEndingIfNeeded();
this.tag(reference);
}
this.lineEndingIfNeeded();
this.tag('</li>');
}
if (calls.length > 0) {
this.lineEndingIfNeeded();
this.tag('</ol>');
this.lineEndingIfNeeded();
this.tag('</section>');
}
}
}
};
}