markdown-it-citations
Version:
pandoc compatible citations for markdown-it
132 lines (131 loc) • 5.52 kB
JavaScript
const Citations = (md, options) => {
const regexes = {
citation: /^([^^-]|[^^].+?)?(-)?@([\w][\w:.#$%&\-+?<>~\/]*)(.+)?$/,
inText: /^@([\w][\w:.#$%&\-+?<>~\/]*)(\s*)(\[)?/,
allowedBefore: /^[^a-zA-Z.0-9]$/
};
md.inline.ruler.after("emphasis", "citation", (state, silent) => {
let max = state.posMax;
const char = state.src.charCodeAt(state.pos);
if (char == 64 && (state.pos == 0 || regexes.allowedBefore.test(state.src.slice(state.pos - 1, state.pos)))) {
const match = state.src.slice(state.pos).match(regexes.inText);
if (match) {
const citeproc = options?.citeproc ? state.env.citeproc || (state.env.citeproc = options?.citeproc(state.env)) : void 0;
let citation = {
citationId: match[1],
citationPrefix: [],
citationSuffix: [],
citationMode: "AuthorInText",
citationNoteNum: state.env.noteNum = (state.env.noteNum || 0) + 1,
citationHash: 0
};
let token;
if (!silent) {
token = state.push("cite_open", "span", 1);
token.attrSet("class", "citation");
token.attrSet("data-cites", match[1]);
}
if (match[3]) {
const suffixStart = state.pos + match[0].length;
const suffixEnd = state.md.helpers.parseLinkLabel(state, suffixStart);
const charAfter = state.src.codePointAt(suffixEnd + 1);
if (suffixEnd > 0 && charAfter != 40 && charAfter != 91) {
const suffix = state.src.slice(suffixStart, suffixEnd);
citation.citationSuffix = state.md.parseInline(suffix, state.env);
state.pending += match[0] + suffix + "]";
state.pos += match[0].length + suffixEnd - suffixStart + 1;
} else {
state.pending += "@" + match[1];
state.pos += match[0].length - match[2].length - match[3].length;
}
} else {
state.pos += match[0].length - match[2].length;
}
if (token) {
token.meta = citeproc?.appendCluster([citation]);
state.pushPending();
state.push("cite_close", "span", -1);
}
return true;
}
} else if (char == 91) {
const end = state.md.helpers.parseLinkLabel(state, state.pos);
const charAfter = state.src.codePointAt(end + 1);
if (end > 0 && charAfter != 40 && charAfter != 91) {
const str = state.src.slice(state.pos + 1, end);
const parts = str.split(";").map((x) => x.match(regexes.citation));
if (parts.indexOf(null) >= 0) {
return false;
} else {
const citeproc = options?.citeproc ? state.env.citeproc || (state.env.citeproc = options?.citeproc(state.env)) : void 0;
const cites = parts.map((x) => ({
citationId: x[3],
citationPrefix: x[1] ? state.md.parseInline(x[1], state.env) : [],
citationSuffix: x[4] ? state.md.parseInline(x[4], state.env) : [],
citationMode: x[2] ? "SuppressAuthor" : "NormalCitation",
citationNoteNum: state.env.noteNum = (state.env.noteNum || 0) + 1,
citationHash: 0
}));
if (!silent) {
const token = state.push("cite_open", "span", 1);
token.meta = citeproc?.appendCluster(cites);
token.attrSet("class", "citation");
token.attrSet("data-cites", cites.map((x) => x.citationId).join(" "));
state.pending = state.src.slice(state.pos, end + 1);
state.push("cite_close", "span", -1);
}
state.pos = end + 1;
return true;
}
}
return false;
}
return false;
});
if (options?.citeproc) {
md.renderer.rules["cite_open"] = (tkns, idx, opts, env, slf) => {
const citeproc = env.citeproc || options.citeproc(env);
tkns[idx + 1].content = "";
return citeproc.renderCluster(tkns[idx].meta, slf);
};
md.renderer.rules["cite_close"] = () => "";
if (!options?.["suppress-bibliography"]) {
md.core.ruler.push("bibliography", (state) => {
if (!state.inlineMode) {
const i = state.tokens.findIndex((tk) => tk.attrGet("id") == "refs");
if (i >= 0) {
const refsOpen = state.tokens[i];
if (refsOpen.nesting != 1) {
return false;
}
let j = i + 1;
while (j < state.tokens.length) {
if (state.tokens[j].tag == refsOpen.tag && state.tokens[j].level == refsOpen.level && state.tokens[j].nesting == -1) {
state.tokens.splice(j, 0, new state.Token("bibliography", "", 0));
return true;
}
j += 1;
}
return false;
} else {
const refsOpen = new state.Token("refs_container", "div", 1);
refsOpen.attrSet("id", "refs");
state.tokens.push(refsOpen);
state.tokens.push(new state.Token("bibliography", "", 0));
state.tokens.push(new state.Token("refs_container", "div", -1));
return true;
}
} else
return false;
});
md.renderer.rules["bibliography"] = (tks, idx, opts, env) => {
const citeproc = options?.citeproc ? env.citeproc || (env.citeproc = options?.citeproc(env)) : void 0;
return citeproc?.renderBibliography() || "<NO CITEPROC CONFIGURED>";
};
}
}
};
var src_default = Citations;
export {
src_default as default
};