From 3b7a6a46d888b0dd3f5c632cd70bc60f29dbaa7d Mon Sep 17 00:00:00 2001 From: Arve Seljebu Date: Sun, 1 Dec 2024 16:02:07 +0100 Subject: [PATCH] 4.3.0 --- markdown-it-attrs.browser.js | 416 ++++++++++++++++++++++++++++------- package-lock.json | 4 +- package.json | 2 +- 3 files changed, 340 insertions(+), 82 deletions(-) diff --git a/markdown-it-attrs.browser.js b/markdown-it-attrs.browser.js index acc681b..5115ad3 100644 --- a/markdown-it-attrs.browser.js +++ b/markdown-it-attrs.browser.js @@ -1,22 +1,82 @@ (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.markdownItAttrs = 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 void} transform + * + * @typedef {Object} MatchedResult + * @property {boolean} match true means matched + * @property {number?} j postion index number of Array<{@link Token}> + * + * @typedef {(str: string) => boolean} DetectingStrRule + * + * @typedef {Object} DetectingRule rule for testing {@link Token}'s properties + * @property {number=} shift offset index number of Array<{@link Token}> + * @property {number=} position fixed index number of Array<{@link Token}> + * @property {(string | DetectingStrRule)=} type + * @property {(string | DetectingStrRule)=} tag + * @property {DetectingRule[]=} children + * @property {(string | DetectingStrRule)=} content + * @property {(string | DetectingStrRule)=} markup + * @property {(string | DetectingStrRule)=} info + * @property {Nesting=} nesting + * @property {number=} level + * @property {boolean=} block + * @property {boolean=} hidden + * @property {AttributePair[]=} attrs + * @property {SourceLineInfo[]=} map + * @property {any=} meta + */ + +/** @type {Options} */ var defaultOptions = { leftDelimiter: '{', rightDelimiter: '}', allowedAttributes: [] }; + +/** + * @param {MarkdownIt} md + * @param {Options=} options_ + */ module.exports = function attributes(md, options_) { var options = Object.assign({}, defaultOptions); options = Object.assign(options, options_); var patterns = patternsConfig(options); + + /** + * @param {StateCore} state + */ function curlyAttrs(state) { var tokens = state.tokens; var _loop = function _loop(i) { - for (var p = 0; p < patterns.length; p++) { - var pattern = patterns[p]; + var _loop2 = function _loop2(_p) { + var pattern = patterns[_p]; var j = null; // position of child with offset 0 var match = pattern.tests.every(function (t) { var res = test(tokens, i, t); @@ -29,9 +89,13 @@ module.exports = function attributes(md, options_) { pattern.transform(tokens, i, j); if (pattern.name === 'inline attributes' || pattern.name === 'inline nesting 0') { // retry, may be several inline attributes - p--; + _p--; } } + p = _p; + }; + for (var p = 0; p < patterns.length; p++) { + _loop2(p); } }; for (var i = 0; i < tokens.length; i++) { @@ -44,17 +108,17 @@ module.exports = function attributes(md, options_) { /** * Test if t matches token stream. * - * @param {array} tokens + * @param {Token[]} tokens * @param {number} i - * @param {object} t Test to match. - * @return {object} { match: true|false, j: null|number } + * @param {DetectingRule} t + * @returns {MatchedResult} */ function test(tokens, i, t) { + /** @type {MatchedResult} */ var res = { match: false, j: null // position of child }; - var ii = t.shift !== undefined ? i + t.shift : t.position; if (t.shift !== undefined && ii < 0) { // we should never shift to negative indexes (rolling around to back of array) @@ -65,27 +129,26 @@ function test(tokens, i, t) { if (token === undefined) { return res; } - var _loop2 = function _loop2() { - var key = _Object$keys[_i]; - if (key === 'shift' || key === 'position') { - return "continue"; - } - if (token[key] === undefined) { - return { - v: res - }; - } - if (key === 'children' && isArrayOfObjects(t.children)) { - var _ret2 = function () { + var _loop3 = function _loop3() { + var key = _Object$keys[_i]; + if (key === 'shift' || key === 'position') { + return 0; // continue + } + if (token[key] === undefined) { + return { + v: res + }; + } + if (key === 'children' && isArrayOfObjects(t.children)) { if (token.children.length === 0) { return { - v: { - v: res - } + v: res }; } var match; + /** @type {DetectingRule[]} */ var childTests = t.children; + /** @type {Token[]} */ var children = token.children; if (childTests.every(function (tt) { return tt.position !== undefined; @@ -100,72 +163,66 @@ function test(tokens, i, t) { res.j = j >= 0 ? j : children.length + j; } } else { - var _loop3 = function _loop3(_j) { + var _loop4 = function _loop4(_j) { match = childTests.every(function (tt) { return test(children, _j, tt).match; }); if (match) { res.j = _j; // all tests true, continue with next key of pattern t - return "break"; + return 1; // break } }; for (var _j = 0; _j < children.length; _j++) { - var _ret3 = _loop3(_j); - if (_ret3 === "break") break; + if (_loop4(_j)) break; } } if (match === false) { - return { - v: { - v: res - } - }; - } - return { - v: "continue" - }; - }(); - if (_typeof(_ret2) === "object") return _ret2.v; - } - switch (_typeof(t[key])) { - case 'boolean': - case 'number': - case 'string': - if (token[key] !== t[key]) { - return { - v: res - }; - } - break; - case 'function': - if (!t[key](token[key])) { return { v: res }; } - break; - case 'object': - if (isArrayOfFunctions(t[key])) { - var r = t[key].every(function (tt) { - return tt(token[key]); - }); - if (r === false) { + return 0; // continue + } + switch (_typeof(t[key])) { + case 'boolean': + case 'number': + case 'string': + if (token[key] !== t[key]) { return { v: res }; } break; - } - // fall through for objects !== arrays of functions - default: - throw new Error("Unknown type of pattern test (key: ".concat(key, "). Test should be of type boolean, number, string, function or array of functions.")); - } - }; + case 'function': + if (!t[key](token[key])) { + return { + v: res + }; + } + break; + case 'object': + if (isArrayOfFunctions(t[key])) { + var r = t[key].every(function (tt) { + return tt(token[key]); + }); + if (r === false) { + return { + v: res + }; + } + break; + } + // fall through for objects !== arrays of functions + default: + throw new Error("Unknown type of pattern test (key: ".concat(key, "). Test should be of type boolean, number, string, function or array of functions.")); + } + }, + _ret; for (var _i = 0, _Object$keys = Object.keys(t); _i < _Object$keys.length; _i++) { - var _ret = _loop2(); - if (_ret === "continue") continue; - if (_typeof(_ret) === "object") return _ret.v; + _ret = _loop3(); + if (_ret === 0) continue; + if (_ret) return _ret.v; } // no tests returned false -> all tests returns true @@ -186,14 +243,19 @@ function isArrayOfFunctions(arr) { /** * Get n item of array. Supports negative n, where -1 is last * element in array. - * @param {array} arr + * @param {Token[]} arr * @param {number} n + * @returns {Token=} */ function get(arr, n) { return n >= 0 ? arr[n] : arr[arr.length + n]; } -// get last element of array, safe - returns {} if not found +/** + * get last element of array, safe - returns {} if not found + * @param {DetectingRule[]} arr + * @returns {DetectingRule} + */ function last(arr) { return arr.slice(-1)[0] || {}; } @@ -206,6 +268,11 @@ function last(arr) { * then run transform. */ var utils = require('./utils.js'); + +/** + * @param {import('.').Options} options + * @returns {import('.').CurlyAttrsPattern[]} + */ module.exports = function (options) { var __hr = new RegExp('^ {0,3}[-*_]{3,} ?' + utils.escapeRegExp(options.leftDelimiter) + '[^' + utils.escapeRegExp(options.rightDelimiter) + ']'); return [{ @@ -250,6 +317,9 @@ module.exports = function (options) { content: utils.hasDelimiters('start', options) }] }], + /** + * @param {!number} j + */ transform: function transform(tokens, i, j) { var token = tokens[i].children[j]; var endChar = token.content.indexOf(options.rightDelimiter); @@ -293,6 +363,136 @@ module.exports = function (options) { // remove

{.c}

tokens.splice(i + 1, 3); } + }, { + /** + * | A | B | + * | -- | -- | + * | 1 | 2 | + * + * | C | D | + * | -- | -- | + * + * only `| A | B |` sets the colsnum metadata + */ + name: 'tables thead metadata', + tests: [{ + shift: 0, + type: 'tr_close' + }, { + shift: 1, + type: 'thead_close' + }, { + shift: 2, + type: 'tbody_open' + }], + transform: function transform(tokens, i) { + var tr = utils.getMatchingOpeningToken(tokens, i); + var th = tokens[i - 1]; + var colsnum = 0; + var n = i; + while (--n) { + if (tokens[n] === tr) { + tokens[n - 1].meta = Object.assign({}, tokens[n + 2].meta, { + colsnum: colsnum + }); + break; + } + colsnum += (tokens[n].level === th.level && tokens[n].type === th.type) >> 0; + } + tokens[i + 2].meta = Object.assign({}, tokens[i + 2].meta, { + colsnum: colsnum + }); + } + }, { + /** + * | A | B | C | D | + * | -- | -- | -- | -- | + * | 1 | 11 | 111 | 1111 {rowspan=3} | + * | 2 {colspan=2 rowspan=2} | 22 | 222 | 2222 | + * | 3 | 33 | 333 | 3333 | + */ + name: 'tables tbody calculate', + tests: [{ + shift: 0, + type: 'tbody_close', + hidden: false + }], + /** + * @param {number} i index of the tbody ending + */ + transform: function transform(tokens, i) { + /** index of the tbody beginning */ + var idx = i - 2; + while (idx > 0 && 'tbody_open' !== tokens[--idx].type); + var calc = tokens[idx].meta.colsnum >> 0; + if (calc < 2) { + return; + } + var level = tokens[i].level + 2; + for (var n = idx; n < i; n++) { + if (tokens[n].level > level) { + continue; + } + var token = tokens[n]; + var rows = token.hidden ? 0 : token.attrGet('rowspan') >> 0; + var cols = token.hidden ? 0 : token.attrGet('colspan') >> 0; + if (rows > 1) { + var colsnum = calc - (cols > 0 ? cols : 1); + for (var k = n, num = rows; k < i, num > 1; k++) { + if ('tr_open' == tokens[k].type) { + tokens[k].meta = Object.assign({}, tokens[k].meta); + if (tokens[k].meta && tokens[k].meta.colsnum) { + colsnum -= 1; + } + tokens[k].meta.colsnum = colsnum; + num--; + } + } + } + if ('tr_open' == token.type && token.meta && token.meta.colsnum) { + var max = token.meta.colsnum; + for (var _k = n, _num = 0; _k < i; _k++) { + if ('td_open' == tokens[_k].type) { + _num += 1; + } else if ('tr_close' == tokens[_k].type) { + break; + } + _num > max && (tokens[_k].hidden || hidden(tokens[_k])); + } + } + if (cols > 1) { + /** @type {number[]} index of one row's children */ + var one = []; + /** last index of the row's children */ + var end = n + 3; + /** number of the row's children */ + var _num2 = calc; + for (var _k2 = n; _k2 > idx; _k2--) { + if ('tr_open' == tokens[_k2].type) { + _num2 = tokens[_k2].meta && tokens[_k2].meta.colsnum || _num2; + break; + } else if ('td_open' === tokens[_k2].type) { + one.unshift(_k2); + } + } + for (var _k3 = n + 2; _k3 < i; _k3++) { + if ('tr_close' == tokens[_k3].type) { + end = _k3; + break; + } else if ('td_open' == tokens[_k3].type) { + one.push(_k3); + } + } + var off = one.indexOf(n); + var real = _num2 - off; + real = real > cols ? cols : real; + cols > real && token.attrSet('colspan', real + ''); + for (var _k4 = one.slice(_num2 + 1 - calc - real)[0]; _k4 < end; _k4++) { + tokens[_k4].hidden || hidden(tokens[_k4]); + } + } + } + } }, { /** * *emphasis*{.with attrs=1} @@ -310,6 +510,9 @@ module.exports = function (options) { content: utils.hasDelimiters('start', options) }] }], + /** + * @param {!number} j + */ transform: function transform(tokens, i, j) { var token = tokens[i].children[j]; var content = token.content; @@ -339,6 +542,9 @@ module.exports = function (options) { content: utils.hasDelimiters('only', options) }] }], + /** + * @param {!number} j + */ transform: function transform(tokens, i, j) { var token = tokens[i].children[j]; var content = token.content; @@ -405,6 +611,9 @@ module.exports = function (options) { content: utils.hasDelimiters('end', options) }] }], + /** + * @param {!number} j + */ transform: function transform(tokens, i, j) { var token = tokens[i].children[j]; var content = token.content; @@ -431,6 +640,9 @@ module.exports = function (options) { content: utils.hasDelimiters('only', options) }] }], + /** + * @param {!number} j + */ transform: function transform(tokens, i, j) { var token = tokens[i].children[j]; var attrs = utils.getAttrs(token.content, 0, options); @@ -492,15 +704,16 @@ module.exports = function (options) { } }] }], + /** + * @param {!number} j + */ transform: function transform(tokens, i, j) { var token = tokens[i].children[j]; var content = token.content; var attrs = utils.getAttrs(content, content.lastIndexOf(options.leftDelimiter), options); var ii = i + 1; - do { - if (tokens[ii] && tokens[ii].nesting === -1) { - break; - } + do if (tokens[ii] && tokens[ii].nesting === -1) { + break; } while (ii++ < tokens.length); var openingToken = utils.getMatchingOpeningToken(tokens, ii); utils.addAttrs(attrs, openingToken); @@ -515,14 +728,36 @@ function last(arr) { return arr.slice(-1)[0]; } +/** + * Hidden table's cells and them inline children, + * specially cast inline's content as empty + * to prevent that escapes the table's box model + * @see https://github.com/markdown-it/markdown-it/issues/639 + * @param {import('.').Token} token + */ +function hidden(token) { + token.hidden = true; + token.children && token.children.forEach(function (t) { + return t.content = '', hidden(t), undefined; + }); +} + },{"./utils.js":3}],3:[function(require,module,exports){ "use strict"; +/** + * @typedef {import('.').Token} Token + * @typedef {import('.').Options} Options + * @typedef {import('.').AttributePair} AttributePair + * @typedef {import('.').AllowedAttribute} AllowedAttribute + * @typedef {import('.').DetectingStrRule} DetectingStrRule + */ /** * parse {.class #id key=val} strings * @param {string} str: string to parse - * @param {int} start: where to start parsing (including {) - * @returns {2d array}: [['key', 'val'], ['class', 'red']] + * @param {number} start: where to start parsing (including {) + * @param {Options} options + * @returns {AttributePair[]}: [['key', 'val'], ['class', 'red']] */ exports.getAttrs = function (str, start, options) { // not tab, line feed, form feed, space, solidus, greater than sign, quotation mark, apostrophe and equals sign @@ -613,6 +848,10 @@ exports.getAttrs = function (str, start, options) { var allowedAttributes = options.allowedAttributes; return attrs.filter(function (attrPair) { var attr = attrPair[0]; + + /** + * @param {AllowedAttribute} allowedAttribute + */ function isAllowedAttribute(allowedAttribute) { return attr === allowedAttribute || allowedAttribute instanceof RegExp && allowedAttribute.test(attr); } @@ -624,8 +863,8 @@ exports.getAttrs = function (str, start, options) { /** * add attributes from [['key', 'val']] list - * @param {array} attrs: [['key', 'val']] - * @param {token} token: which token to add attributes + * @param {AttributePair[]} attrs: [['key', 'val']] + * @param {Token} token: which token to add attributes * @returns token */ exports.addAttrs = function (attrs, token) { @@ -649,8 +888,9 @@ exports.addAttrs = function (attrs, token) { * end: 'asdf {.a}' * only: '{.a}' * - * @param {string} where to expect {} curly. start, end or only. - * @return {function(string)} Function which testes if string has curly. + * @param {'start'|'end'|'only'} where to expect {} curly. start, end or only. + * @param {Options} options + * @return {DetectingStrRule} Function which testes if string has curly. */ exports.hasDelimiters = function (where, options) { if (!where) { @@ -667,6 +907,10 @@ exports.hasDelimiters = function (where, options) { if (!str || typeof str !== 'string' || str.length < minCurlyLength) { return false; } + + /** + * @param {string} curly + */ function validCurlyLength(curly) { var isClass = curly.charAt(options.leftDelimiter.length) === '.'; var isId = curly.charAt(options.leftDelimiter.length) === '#'; @@ -708,6 +952,8 @@ exports.hasDelimiters = function (where, options) { /** * Removes last curly from string. + * @param {string} str + * @param {Options} options */ exports.removeDelimiter = function (str, options) { var start = escapeRegExp(options.leftDelimiter); @@ -731,6 +977,8 @@ exports.escapeRegExp = escapeRegExp; /** * find corresponding opening block + * @param {Token[]} tokens + * @param {number} i */ exports.getMatchingOpeningToken = function (tokens, i) { if (tokens[i].type === 'softbreak') { @@ -761,9 +1009,19 @@ var HTML_REPLACEMENTS = { '>': '>', '"': '"' }; + +/** + * @param {string} ch + * @returns {string} + */ function replaceUnsafeChar(ch) { return HTML_REPLACEMENTS[ch]; } + +/** + * @param {string} str + * @returns {string} + */ exports.escapeHtml = function (str) { if (HTML_ESCAPE_TEST_RE.test(str)) { return str.replace(HTML_ESCAPE_REPLACE_RE, replaceUnsafeChar); diff --git a/package-lock.json b/package-lock.json index 221e673..6a348a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "markdown-it-attrs", - "version": "4.2.0", + "version": "4.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "markdown-it-attrs", - "version": "4.2.0", + "version": "4.3.0", "license": "MIT", "devDependencies": { "@babel/core": "^7.20.5", diff --git a/package.json b/package.json index f5042c5..d706d64 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "markdown-it-attrs", - "version": "4.2.0", + "version": "4.3.0", "description": "Add classes, identifiers and attributes to your markdown with {} curly brackets, similar to pandoc's header attributes", "main": "index.js", "license": "MIT",