markdown-it-wikirefs
Version:
markdown-it plugin to add wikirefs (including [[wikilinks]])
920 lines (836 loc) • 31.7 kB
JavaScript
// markdown-it-wikirefs v0.0.2 - https://github.com/wikibonsai/markdown-it-wikirefs.git
import path from 'path';
import { merge } from 'lodash';
import * as wikirefs from 'wikirefs';
// perform any file preparations before we do anything --
// (like flush the current file's entry in index if there is one)
const prep_file = (md, opts) => {
// insert the 'prep_file' token after all wikirefs have been tokenized.
// this way, when 'prep_file' calls 'unshift' (see above) it will place the
// render rule in front of all other tokens. otherwise, it might get shuffled
// into an index other than the first one.
md.core.ruler.push('prep_file', prep_file);
function prep_file(state) {
if (opts.prepFile) {
const tok = new state.Token('trigger_prep_file', '', 0);
state.tokens.unshift(tok);
}
}
md.renderer.rules.trigger_prep_file = trigger_prep_file;
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function trigger_prep_file(tokens, index, mdOpts, env) {
if (opts.prepFile) {
opts.prepFile(env);
}
return '';
}
};
const wikiattrs = (md, opts) => {
// rulers
// '.getRules('attrs')' is really testing for markdown-it-'caml'
// 'attrs' is added as an extra dummy 'alt' specifically for this purpose
// (directly accessing the 'caml' rule would be ideal, but markdown-it
// doesn't seem to have a mechanism for that and not sure how to
// attach a 'name' property to sinon fakes)
const attrRules = md.block.ruler.getRules('attrs');
if (opts.attrs.render && attrRules.length === 0) {
// the 'attrbox' rule is the midpoint between the parse and render rules.
md.core.ruler.after('inline', 'wiki_attrbox', wiki_attrbox);
}
// should execute just after 'markdown-it-caml':
// [ ..., 'hr', 'caml', 'wikiattr', 'list', ... ]
md.block.ruler.before('list', 'wikiattr', wikiattr, {
alt: ['paragraph', 'reference', 'blockquote', 'list', 'attrs']
}); // in case bugs show up: [ 'paragraph', 'reference', 'blockquote', 'list' ]
// render
md.renderer.rules.metadata_wikiattr = metadata_wikiattr;
md.renderer.rules.wikiattr_open = wikiattr_open;
md.renderer.rules.wikiattr_key = wikiattr_key;
md.renderer.rules.wikiattr_val = wikiattr_val;
md.renderer.rules.wikiattr_close = wikiattr_close;
// rulers
function wikiattr(state, startLine, endLine, silent) {
// return false cases //
////
// skip indented code blocks
// from: https://github.com/markdown-it/markdown-it/blob/df4607f1d4d4be7fdc32e71c04109aea8cc373fa/lib/rules_block/list.js#L132
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) {
return false;
}
////
// 'wikiattrs' must be at the top-most-level
// todo: how to interrupt?
// !('list' | 'blockquote' | 'reference' | 'footnote')
if (state.parentType !== 'root' && state.parentType !== 'paragraph') {
return false;
}
////
// check for matches
// 'bMarks' = beginning of line markers
// 'eMarks' = end of line markers
let pos = state.bMarks[startLine];
let max = state.eMarks[startLine];
const thisChunk = state.src.substring(pos, max);
const lineOneMatch = wikirefs.RGX.ATTR_LINE.TYPE.exec(thisChunk);
// no match
if (lineOneMatch === null) {
return false;
}
// is in a list item
// note: this is only necessary for unprefixed wikiattrs
// todo: keep an eye on this...might cause problems...
if (lineOneMatch[0].indexOf('- ') === 0 || lineOneMatch[0].indexOf('* ') === 0 || lineOneMatch[0].indexOf('+ ') === 0) {
return false;
}
// "Don't run any pairs in validation mode":
// 'silent' is used when this rule is being checked against
// in another rule to see whether or not the other rule should
// kick out for this (or some other) one. return 'true' to
// signify that the kick out should happen
if (silent) {
return true;
}
// handle match and return true //
let iterLine = 0;
let m;
const curFilenames = [];
const attrTypeText = lineOneMatch[1].trim();
const filenamesText = lineOneMatch[2];
// links
// - comma-separated list; '2' would be the first wikilink's filename
if (filenamesText !== null && filenamesText !== undefined) {
iterLine += 1;
const gottaCatchEmAll = new RegExp(`${wikirefs.RGX.WIKI.BASE.source}`, 'ig');
// loop through all matches from 'g'lobal regex
// do-while: https://stackoverflow.com/a/6323598
do {
m = gottaCatchEmAll.exec(lineOneMatch[0]);
if (m !== null) {
// m[0]: full match;
// m[1]: filename;
curFilenames.push(m[1]);
}
} while (m);
// - markdown-style list
} else {
// loop through each markdown-style list item
// do-while: https://stackoverflow.com/a/6323598
do {
// increment
iterLine += 1;
pos = state.bMarks[startLine + iterLine];
max = state.eMarks[startLine + iterLine];
const thisSubChunk = state.src.substring(pos, max);
m = wikirefs.RGX.ATTR_LINE.LIST_ITEM.exec(thisSubChunk);
if (m !== null) {
// m[0]: full match;
// m[1]: bullet type;
// m[2]: wikistring;
// m[3]: filename;
curFilenames.push(m[3]);
}
} while (m);
}
// set 'state.env.attrs' to trigger tokens -- if valid.
if (curFilenames.length === 0) {
return false;
} else {
// init
if (!state.env.attrs) {
state.env.attrs = {};
}
if (!state.env.attrs[attrTypeText]) {
state.env.attrs[attrTypeText] = [];
}
// prep renderables
for (const fname of curFilenames) {
state.env.attrs[attrTypeText].push({
type: 'wiki',
filename: fname
});
}
// metadata
if (opts.addAttr) {
const tok = new state.Token('metadata_wikiattr', '', 0);
state.tokens.push(tok);
tok.attrSet('key', attrTypeText);
// note: tokens technically should only accept 'string' or 'null'...but an array of strings works so nicely here...
tok.attrSet('vals', state.env.attrs[attrTypeText].map(item => item.filename));
}
// continue; increment position
state.line += iterLine;
return true;
}
}
function wiki_attrbox(state) {
if (!state.env.attrs || Object.keys(state.env.attrs).length === 0) {
return;
}
const tokens = [];
// open //
const tokOpen = new state.Token('wikiattr_open', '', 0);
// tokOpen.map = [startLine, iterLine];
tokens.push(tokOpen);
// body //
for (const attrtype in state.env.attrs) {
// key / attrtype
const tokType = new state.Token('wikiattr_key', '', 0);
tokType.attrSet('key', attrtype);
tokens.push(tokType);
// values / items
for (const item of state.env.attrs[attrtype]) {
const tokItem = new state.Token('wikiattr_val', '', 1);
if (item.type === 'wiki') {
const filename = item.filename;
if (!filename) {
continue;
}
tokItem.attrSet('key', attrtype);
tokItem.attrSet('val', filename);
}
tokens.push(tokItem);
}
}
// close //
const tokClose = new state.Token('wikiattr_close', '', 0);
tokens.push(tokClose);
// add infobox to **front** of token stream
if (tokens) {
state.tokens = tokens.concat(state.tokens);
}
}
// tokens
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function metadata_wikiattr(tokens, index, mdOpts, env) {
const token = tokens[index];
const attrtype = token.attrGet('key');
// @ts-expect-error: forcing array -- technically not supposed to, but it works so nicely here (see note above)
const filenames = token.attrGet('vals');
if (attrtype && filenames && opts.addAttr) {
for (const filename of filenames) {
opts.addAttr(env, attrtype, filename);
}
}
return '';
}
// render
// example render output:
//
// <aside class="attrbox">
// <span class="attrbox-title">Attributes</span>
// <dl>
// <dt>attrtype</dt>
// <dd><a class="attr wiki attrtype doctype" href="/tests/fixtures/fname-a" data-href="/tests/fixtures/fname-a">title a</a></dd>
// <dd><a class="attr wiki attrtype doctype" href="/tests/fixtures/fname-b" data-href="/tests/fixtures/fname-b">title b</a></dd>
// <dd><a class="attr wiki attrtype doctype" href="/tests/fixtures/fname-c" data-href="/tests/fixtures/fname-c">title c</a></dd>
// ...
// </dl>
// </aside>
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function wikiattr_open(tokens, index, mdOpts, env) {
return `<aside class="${opts.cssNames.attrbox}">\n<span class="${opts.cssNames.attrboxTitle}">${opts.attrs.title}</span>\n<dl>\n`;
}
// attr : key : attrtype
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function wikiattr_key(tokens, index, mdOpts, env) {
const token = tokens[index];
const attrtype = token.attrGet('key');
return attrtype ? `<dt>${attrtype}</dt>\n` : '<dt>attrtype error</dt>\n';
}
// attr : val(s) : item(s)
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function wikiattr_val(tokens, index, mdOpts, env) {
// load
const token = tokens[index];
if (token === null) {
return '<dd>token error</dd>\n';
}
const filename = token.attrGet('val');
if (filename === null) {
return '<dd>filename error</dd>\n';
}
const htmlHref = opts.resolveHtmlHref(env, filename);
const htmlText = opts.resolveHtmlText(env, filename) ? opts.resolveHtmlText(env, filename) : filename;
const doctype = opts.resolveDocType ? opts.resolveDocType(env, filename) : '';
// render
// invalid
if (htmlHref === undefined) {
const wikitext = filename !== null ? filename : 'error';
return `<dd><a class="${opts.cssNames.attr} ${opts.cssNames.wiki} ${opts.cssNames.invalid}">[[${wikitext}]]</a></dd>\n`;
// valid
} else {
const attrtype = token.attrGet('key');
// css
const cssClassArray = [];
// 'attr'
if (attrtype !== null && attrtype !== undefined) {
cssClassArray.push(opts.cssNames.attr);
}
// 'wiki'
cssClassArray.push(opts.cssNames.wiki);
if (attrtype !== null && attrtype !== undefined) {
const attrTypeSlug = attrtype.trim().toLowerCase().replace(/ /g, '-').replace(/[^\w-]+/g, '');
cssClassArray.push(opts.cssNames.reftype + attrTypeSlug);
}
// '<doctype>'
if (doctype !== null && doctype !== undefined && doctype.length !== 0) {
const docTypeSlug = doctype.trim().toLowerCase().replace(/ /g, '-').replace(/[^\w-]+/g, '');
cssClassArray.push(opts.cssNames.doctype + docTypeSlug);
}
const css = cssClassArray.join(' ');
return `<dd><a class="${css}" href="${opts.baseUrl + htmlHref}" data-href="${opts.baseUrl + htmlHref}">${htmlText}</a></dd>\n`;
}
}
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function wikiattr_close(tokens, index, mdOpts, env) {
delete env.cur_attr_type;
return '</dl>\n</aside>\n';
}
};
const wikilinks = (md, opts) => {
// rulers
md.inline.ruler.after('link', 'wikilink', wikilink);
// render
md.renderer.rules.metadata_wikilink = metadata_wikilink;
md.renderer.rules.wikilink_open = wikilink_open;
md.renderer.rules.wikilink_body = wikilink_body;
md.renderer.rules.wikilink_close = wikilink_close;
// rulers
function wikilink(state, silent) {
const srcText = state.src.substring(state.pos);
// process match info
const match = wikirefs.RGX.WIKI.LINK.exec(srcText);
if (match == null || match.length < 1 || match.index !== 0) {
return false;
}
// uncomment in case of skip wikiembeds
// const embedChar: string = state.src.substring(state.pos - 1, state.pos);
// if (embedChar === '!') {
// return false;
// }
const matchText = match[0];
const linkTypeText = match[1] ? match[1].trim() : match[1];
const filenameText = match[2];
// const headerText : string = match[4];
// const blockText : string = match[5];
const labelText = match[3];
// handle early kick-out if we've hit a stop-char early //
// untyped
if (!linkTypeText && srcText[0] !== '[') {
return false;
}
// typed
if (linkTypeText && srcText[0] !== ':') {
return false;
}
// "Don't run any pairs in validation mode":
// 'silent' is used when this rule is being checked against
// in another rule to see whether or not the other rule should
// kick out for this (or some other) one. return 'true' to
// signify that the kick out should happen
if (silent) {
return false;
}
let token;
// open //
token = state.push('wikilink_open', 'wikilink', 0);
token.attrSet('filename', filenameText);
if (linkTypeText !== null && linkTypeText !== undefined && linkTypeText.length !== 0) {
token.attrSet('linktype', linkTypeText);
}
// body //
token = state.push('wikilink_body', '', 0);
token.attrSet('filename', filenameText);
token.attrSet('matchText', matchText);
if (labelText) {
token.attrSet('label', labelText);
}
// close //
token = state.push('wikilink_close', 'wikilink', 0);
// metadata
if (opts.addLink) {
token = state.push('metadata_wikilink', 'wikilink', 0);
token.attrSet('linktype', linkTypeText);
token.attrSet('filename', filenameText);
}
// continue; increment position
state.pos += matchText.length;
return true;
}
// render
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function metadata_wikilink(tokens, index, mdOpts, env) {
const token = tokens[index];
if (token === null) {
return 'token error';
}
// don't let 'linktype' be 'null' -- untyped wikilinks have empty strings for 'linktype'
const linktype = token.attrGet('linktype') ? token.attrGet('linktype') : '';
const filename = token.attrGet('filename');
if (linktype !== null && filename !== null && opts.addLink) {
opts.addLink(env, linktype, filename);
}
return '';
}
// render
// typed
//
// <a class="wiki link type linktype doctype" href="/tests/fixtures/fname-a" data-href="/tests/fixtures/fname-a">title a</a>
//
// untyped
//
// <a class="wiki link doctype" href="/tests/fixtures/fname-a" data-href="/tests/fixtures/fname-a">title a</a>
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function wikilink_open(tokens, index, mdOpts, env) {
// load
const token = tokens[index];
const invalidOpen = `<a class="${opts.cssNames.wiki} ${opts.cssNames.link} ${opts.cssNames.invalid}">`;
if (token === null) {
return invalidOpen;
}
const filename = token.attrGet('filename');
const linktype = token.attrGet('linktype');
if (filename === null) {
return invalidOpen;
}
const htmlHref = opts.resolveHtmlHref(env, filename);
const doctype = opts.resolveDocType ? opts.resolveDocType(env, filename) : '';
// render
// invalid
if (htmlHref === undefined) {
return invalidOpen;
// valid
} else {
// build css string
const cssClassArray = [];
// wikilink
cssClassArray.push(opts.cssNames.wiki);
cssClassArray.push(opts.cssNames.link);
// linktype
if (linktype !== null && linktype !== undefined && linktype.length !== 0) {
cssClassArray.push(opts.cssNames.type);
const linkTypeSlug = linktype.trim().toLowerCase().replace(/ /g, '-').replace(/[^\w-]+/g, '');
cssClassArray.push(opts.cssNames.reftype + linkTypeSlug);
}
// doctype
if (doctype) {
const docTypeSlug = doctype.trim().toLowerCase().replace(/ /g, '-').replace(/[^\w-]+/g, '');
cssClassArray.push(opts.cssNames.doctype + docTypeSlug);
}
const css = cssClassArray.join(' ');
return `<a class="${css}" href="${opts.baseUrl + htmlHref}" data-href="${opts.baseUrl + htmlHref}">`;
}
}
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function wikilink_body(tokens, index, mdOpts, env) {
// load
const token = tokens[index];
if (token === null) {
return 'token error';
}
const filename = token.attrGet('filename');
const labelText = token.attrGet('label');
const matchText = token.attrGet('matchText');
if (filename === null) {
return 'filename error';
}
const htmlHref = opts.resolveHtmlHref(env, filename);
const htmlText = opts.resolveHtmlText(env, filename);
// render
// invalid
if (!htmlHref && matchText) {
token.content = matchText;
// valid
} else {
// html text, order of precedence
for (const content of [labelText, htmlText, filename]) {
if (typeof content === 'string' && content.length > 0) {
token.content = content;
break;
}
}
}
return token.content;
}
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function wikilink_close(tokens, index, mdOpts, env) {
return '</a>';
}
};
const wikiembeds = (md, opts) => {
// rulers
const wikiLinkRule = md.block.ruler.getRules('wikilink');
if (wikiLinkRule.length === 0) {
md.inline.ruler.before('link', 'wikiembed', wikiembed);
} else {
md.inline.ruler.before('wikilink', 'wikiembed', wikiembed);
}
// render
md.renderer.rules.metadata_wikiembed = metadata_wikiembed;
md.renderer.rules.wikiembed_open = wikiembed_open;
md.renderer.rules.wikiembed_close = wikiembed_close;
// body
md.renderer.rules.wikiembed_content_body_media = wikiembed_content_body_media;
// markdown
// title
md.renderer.rules.wikiembed_title_open = wikiembed_title_open;
md.renderer.rules.wikiembed_title_body = wikiembed_title_body;
md.renderer.rules.wikiembed_title_close = wikiembed_title_close;
// link
md.renderer.rules.wikiembed_link_open = wikiembed_link_open;
md.renderer.rules.wikiembed_link_body = wikiembed_link_body;
md.renderer.rules.wikiembed_link_close = wikiembed_link_close;
// content
md.renderer.rules.wikiembed_content_open = wikiembed_content_open;
md.renderer.rules.wikiembed_content_body_md = wikiembed_content_body_md;
md.renderer.rules.wikiembed_content_close = wikiembed_content_close;
// rulers
// parsed as 'inline', but renders as a pseudo-'block'
function wikiembed(state, silent) {
const srcText = state.src.substring(state.pos);
// process match info
const match = wikirefs.RGX.WIKI.EMBED.exec(srcText);
if (match == null || match.length < 1 || match.index !== 0) {
return false;
}
const matchText = match[0];
const filenameText = match[1];
// "Don't run any pairs in validation mode":
// 'silent' is used when this rule is being checked against
// in another rule to see whether or not the other rule should
// kick out for this (or some other) one. return 'true' to
// signify that the kick out should happen
if (silent) {
return false;
}
let token;
// open embed //
token = state.push('wikiembed_open', 'wikiembed', 0);
token.attrSet('filename', filenameText);
// body
if (wikirefs.isMedia(filenameText)) {
token = state.push('wikiembed_content_body_media', 'wikiembed', 0);
token.attrSet('filename', filenameText);
// process as markdown (may contain invalid media extensions)
} else {
// title //
token = state.push('wikiembed_title_open', 'wikiembed', 0);
token = state.push('wikiembed_title_body', 'wikiembed', 0);
token.attrSet('filename', filenameText);
token = state.push('wikiembed_title_close', 'wikiembed', 0);
// link //
token = state.push('wikiembed_link_open', 'wikiembed', 0);
token.attrSet('filename', filenameText);
token = state.push('wikiembed_link_body', 'wikiembed', 0);
token.attrSet('filename', filenameText);
token = state.push('wikiembed_link_close', 'wikiembed', 0);
// content //
token = state.push('wikiembed_content_open', 'wikiembed', 0);
token = state.push('wikiembed_content_body_md', 'wikiembed', 0);
token.attrSet('filename', filenameText);
token = state.push('wikiembed_content_close', 'wikiembed', 0);
}
// close embed //
token = state.push('wikiembed_close', 'wikiembed', 0);
token.attrSet('filename', filenameText);
// metadata
if (opts.addEmbed) {
token = state.push('metadata_wikiembed', 'wikiembed', 0);
token.attrSet('filename', filenameText);
}
// continue; increment position
state.pos += matchText.length;
return true;
}
// render
// markdown embeds:
//
// <p>
// <div class="embed-wrapper">
// <div class="embed-title">
// <a class="wiki embed doctype" href="/tests/fixtures/embed-doc" data-href="/tests/fixtures/embed-doc">
// embedded document
// </a>
// </div>
// <div class="embed-link">
// <a class="embed-link-icon" href="/tests/fixtures/embed-doc" data-href="/tests/fixtures/embed-doc">
// <i class="link-icon"></i>
// </a>
// </div>
// <div class="embed-content">
// <p>Here is some content.</p>
// </div>
// </div>
// </p>
// media embeds (audio, img, video):
// audio:
//
// <p>
// <span class="embed-media" src="audio.mp3" alt="audio.mp3">
// <audio class="embed-audio" controls type="audio/mp3" src="/tests/fixtures/audio.mp3"></audio>
// </span>
// </p>
// image:
//
// <p>
// <span class="embed-media" src="image.png" alt="image.png">
// <img class="embed-image" src="/tests/fixtures/image.png">
// </span>
// </p>
// video:
//
// <p>
// <span class="embed-media" src="video.mp4" alt="video.mp4">
// <video class="embed-audio" controls type="video/mp4" src="/tests/fixtures/video.mp4"></video>
// </span>
// </p>
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function metadata_wikiembed(tokens, index, mdOpts, env) {
const token = tokens[index];
if (token === null) {
return 'token error';
}
// don't let 'linktype' be 'null' -- untyped wikiembeds have empty strings for 'linktype'
const filename = token.attrGet('filename');
if (filename !== null && opts.addEmbed) {
opts.addEmbed(env, filename);
}
return '';
}
// embed
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function wikiembed_open(tokens, index, mdOpts, env) {
// load
const token = tokens[index];
const filename = token.attrGet('filename');
if (filename === null) {
return 'filename error';
}
// render
if (wikirefs.isMedia(filename)) {
return '\n<p>\n';
} else {
return `\n<p>\n<div class="${opts.cssNames.embedWrapper}">\n`;
}
}
// title
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function wikiembed_title_open(tokens, index, mdOpts, env) {
return `<div class="${opts.cssNames.embedTitle}">\n`;
}
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function wikiembed_title_body(tokens, index, mdOpts, env) {
// load
const token = tokens[index];
if (token === null) {
return 'token error';
}
const filename = token.attrGet('filename');
if (filename === null) {
return 'filename error';
}
const htmlHref = opts.resolveHtmlHref(env, filename);
const htmlText = opts.resolveHtmlText(env, filename) ? opts.resolveHtmlText(env, filename) : filename;
const doctype = opts.resolveDocType ? opts.resolveDocType(env, filename) : '';
// render
if (htmlHref === undefined) {
return `<a class="${opts.cssNames.wiki} ${opts.cssNames.embed} ${opts.cssNames.invalid}">\n${htmlText}\n</a>\n`;
} else {
// build css string
const cssClassArray = [];
cssClassArray.push(opts.cssNames.wiki);
cssClassArray.push(opts.cssNames.embed);
// '<doctype>'
if (doctype !== null && doctype !== undefined && doctype.length !== 0) {
const docTypeSlug = doctype.trim().toLowerCase().replace(/ /g, '-').replace(/[^\w-]+/g, '');
cssClassArray.push(opts.cssNames.doctype + docTypeSlug);
}
const css = cssClassArray.join(' ');
return `<a class="${css}" href="${opts.baseUrl + htmlHref}" data-href="${opts.baseUrl + htmlHref}">\n${htmlText}\n</a>\n`;
}
}
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function wikiembed_title_close(tokens, index, mdOpts, env) {
return '</div>\n';
}
// link
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function wikiembed_link_open(tokens, index, mdOpts, env) {
// load
const token = tokens[index];
const invalidOpen = `<div class="${opts.cssNames.embedLink}">\n<a class="${opts.cssNames.embedLinkIcon} ${opts.cssNames.invalid}">\n`;
if (token === null) {
return invalidOpen;
}
const filename = token.attrGet('filename');
if (filename === null) {
return invalidOpen;
}
// render
const htmlHref = opts.resolveHtmlHref(env, filename);
// invalid
if (htmlHref === undefined) {
return invalidOpen;
// valid
} else {
return `<div class="${opts.cssNames.embedLink}">\n<a class="${opts.cssNames.embedLinkIcon}" href="${opts.baseUrl + htmlHref}" data-href="${opts.baseUrl + htmlHref}">\n`;
}
}
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function wikiembed_link_body(tokens, index, mdOpts, env) {
const token = tokens[index];
// error
if (token === null) {
return 'token error';
}
return `<i class="${opts.cssNames.linkIcon}"></i>\n`;
}
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function wikiembed_link_close(tokens, index, mdOpts, env) {
return '</a>\n</div>\n';
}
// content
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function wikiembed_content_open(tokens, index, mdOpts, env) {
return `<div class="${opts.cssNames.embedContent}">\n`;
}
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function wikiembed_content_body_md(tokens, index, mdOpts, env) {
// load
const token = tokens[index];
if (token === null) {
return 'token error';
}
const filename = token.attrGet('filename');
if (filename === null) {
return opts.embeds.errorContent + '\'' + filename + '\'';
}
const htmlContent = opts.resolveEmbedContent(env, filename);
// render
token.content = htmlContent ? htmlContent : opts.embeds.errorContent + '\'' + filename + '\'';
return token.content + '\n';
}
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function wikiembed_content_body_media(tokens, index, mdOpts, env) {
// load
const token = tokens[index];
if (token === null) {
return 'token error';
}
const filename = token.attrGet('filename');
if (filename === null) {
return 'filename error';
}
const filenameSlug = filename.trim().toLowerCase().replace(/ /g, '-'); //.replace(/[^\w-]+/g, '');
const htmlHref = opts.resolveHtmlHref(env, filename);
const mediaExt = path.extname(filename).toLowerCase();
const mime = path.extname(filename).replace('.', '').toLowerCase();
// render
if (wikirefs.CONST.EXTS.AUD.has(mediaExt)) {
token.content = `<span class="${opts.cssNames.embedMedia}" src="${filenameSlug}" alt="${filenameSlug}">\n`;
token.content += htmlHref ? `<audio class="${opts.cssNames.embedAudio}" controls type="audio/${mime}" src="${htmlHref}"></audio>\n` : `<audio class="${opts.cssNames.embedAudio}" controls type="audio/${mime}"></audio>\n`;
} else if (wikirefs.CONST.EXTS.IMG.has(mediaExt)) {
token.content = `<span class="${opts.cssNames.embedMedia}" src="${filenameSlug}" alt="${filenameSlug}">\n`;
token.content += htmlHref ? `<img class="${opts.cssNames.embedImage}" src="${htmlHref}">\n` : `<img class="${opts.cssNames.embedImage}">\n`;
} else if (wikirefs.CONST.EXTS.VID.has(mediaExt)) {
token.content = `<span class="${opts.cssNames.embedMedia}" src="${filenameSlug}" alt="${filenameSlug}">\n`;
token.content += htmlHref ? `<video class="${opts.cssNames.embedVideo}" controls type="video/${mime}" src="${htmlHref}"></video>\n` : `<video class="${opts.cssNames.embedVideo}" controls type="video/${mime}"></video>\n`;
} else {
// note: this is probably not technically possible (due to 'wikirefs.wikirefs.isMedia()' check)
token.content = `<span class="${opts.cssNames.embedMedia} ${opts.cssNames.invalid}">\n`;
token.content += 'media error\n';
}
return token.content;
}
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function wikiembed_content_close(tokens, index, mdOpts, env) {
return '</div>\n';
}
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function wikiembed_close(tokens, index, mdOpts, env) {
// load
const token = tokens[index];
const filename = token.attrGet('filename');
if (filename === null) {
return 'filename error';
}
// render
if (wikirefs.isMedia(filename)) {
return '</span>\n</p>\n';
} else {
return '</div>\n</p>\n';
}
}
};
// import
// export
function wikirefs_plugin(md, opts) {
// opts
const defaults = {
resolveHtmlText: (env, fname) => fname.replace(/-/g, ' '),
resolveHtmlHref: (env, fname) => {
const extname = wikirefs.isMedia(fname) ? path.extname(fname) : '';
fname = fname.replace(extname, '');
return '/' + fname.trim().toLowerCase().replace(/ /g, '-').replace(/[^\w-]+/g, '') + extname;
},
resolveEmbedContent: (env, fname) => fname + ' content',
baseUrl: '',
cssNames: {
// wiki
wiki: 'wiki',
invalid: 'invalid',
// kinds
attr: 'attr',
link: 'link',
type: 'type',
embed: 'embed',
reftype: 'reftype__',
doctype: 'doctype__',
// attr
attrbox: 'attrbox',
attrboxTitle: 'attrbox-title',
// embed
embedWrapper: 'embed-wrapper',
embedTitle: 'embed-title',
embedLink: 'embed-link',
embedContent: 'embed-content',
embedLinkIcon: 'embed-link-icon',
linkIcon: 'link-icon',
embedMedia: 'embed-media',
embedAudio: 'embed-audio',
embedDoc: 'embed-doc',
embedImage: 'embed-image',
embedVideo: 'embed-video'
},
attrs: {
enable: true,
render: true,
title: 'Attributes'
},
links: {
enable: true
},
embeds: {
enable: true,
title: 'Embed Content',
errorContent: 'Error: Content not found for '
}
};
const fullOpts = merge(defaults, opts);
// by order of execution
if (fullOpts.prepFile) {
prep_file(md, fullOpts);
}
if (fullOpts.attrs && fullOpts.attrs.enable) {
wikiattrs(md, fullOpts);
}
if (fullOpts.links && fullOpts.links.enable) {
wikilinks(md, fullOpts);
}
if (fullOpts.embeds && fullOpts.embeds.enable) {
wikiembeds(md, fullOpts);
}
}
export { wikirefs_plugin as default };
//# sourceMappingURL=index.esm.js.map