Browse Source

var => const, let

pull/979/head
Vitaly Puzrin 1 year ago
parent
commit
88b8669c46
  1. 3
      .eslintrc.yml
  2. 20
      benchmark/benchmark.mjs
  3. 4
      benchmark/implementations/commonmark-reference/index.mjs
  4. 4
      benchmark/implementations/current-commonmark/index.mjs
  5. 2
      benchmark/implementations/current/index.mjs
  6. 2
      benchmark/implementations/markdown-it-2.2.1-commonmark/index.mjs
  7. 6
      benchmark/profile.mjs
  8. 10
      bin/markdown-it.mjs
  9. 28
      lib/common/html_re.mjs
  10. 30
      lib/common/utils.mjs
  11. 19
      lib/helpers/parse_link_destination.mjs
  12. 10
      lib/helpers/parse_link_label.mjs
  13. 20
      lib/helpers/parse_link_title.mjs
  14. 32
      lib/index.mjs
  15. 24
      lib/parser_block.mjs
  16. 10
      lib/parser_core.mjs
  17. 47
      lib/parser_inline.mjs
  18. 59
      lib/renderer.mjs
  19. 28
      lib/ruler.mjs
  20. 70
      lib/rules_block/blockquote.mjs
  21. 7
      lib/rules_block/code.mjs
  22. 21
      lib/rules_block/fence.mjs
  23. 29
      lib/rules_block/heading.mjs
  24. 14
      lib/rules_block/hr.mjs
  25. 17
      lib/rules_block/html_block.mjs
  26. 38
      lib/rules_block/lheading.mjs
  27. 101
      lib/rules_block/list.mjs
  28. 28
      lib/rules_block/paragraph.mjs
  29. 78
      lib/rules_block/reference.mjs
  30. 36
      lib/rules_block/state_block.mjs
  31. 115
      lib/rules_block/table.mjs
  32. 2
      lib/rules_core/block.mjs
  33. 6
      lib/rules_core/inline.mjs
  34. 69
      lib/rules_core/linkify.mjs
  35. 6
      lib/rules_core/normalize.mjs
  36. 14
      lib/rules_core/replacements.mjs
  37. 51
      lib/rules_core/smartquotes.mjs
  38. 11
      lib/rules_core/text_join.mjs
  39. 55
      lib/rules_inline/autolink.mjs
  40. 22
      lib/rules_inline/backticks.mjs
  41. 35
      lib/rules_inline/balance_pairs.mjs
  42. 60
      lib/rules_inline/emphasis.mjs
  43. 21
      lib/rules_inline/entity.mjs
  44. 17
      lib/rules_inline/escape.mjs
  45. 8
      lib/rules_inline/fragments_join.mjs
  46. 14
      lib/rules_inline/html_inline.mjs
  47. 27
      lib/rules_inline/image.mjs
  48. 24
      lib/rules_inline/link.mjs
  49. 36
      lib/rules_inline/linkify.mjs
  50. 8
      lib/rules_inline/newline.mjs
  51. 33
      lib/rules_inline/state_inline.mjs
  52. 41
      lib/rules_inline/strikethrough.mjs
  53. 2
      lib/rules_inline/text.mjs
  54. 15
      lib/token.mjs
  55. 91
      support/demo_template/index.mjs
  56. 22
      support/specsplit.mjs
  57. 6
      test/babelmark-responder.mjs
  58. 4
      test/commonmark.mjs
  59. 2
      test/markdown-it.mjs
  60. 98
      test/misc.mjs
  61. 29
      test/ruler.mjs
  62. 2
      test/token.mjs
  63. 14
      test/utils.mjs

3
.eslintrc.yml

@ -21,6 +21,9 @@ ignorePatterns:
- benchmark/extra/
rules:
no-var: 2
prefer-const: 2
no-const-assign: 2
accessor-pairs: 2
array-bracket-spacing: [ 2, "always", { "singleValue": true, "objectsInArrays": true, "arraysInArrays": true } ]
block-scoped-var: 2

20
benchmark/benchmark.mjs

@ -4,13 +4,13 @@
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
var fs = require('fs');
var util = require('util');
var Benchmark = require('benchmark');
var ansi = require('ansi');
var cursor = ansi(process.stdout);
const fs = require('fs');
const util = require('util');
const Benchmark = require('benchmark');
const ansi = require('ansi');
const cursor = ansi(process.stdout);
var IMPLS = [];
const IMPLS = [];
for (const name of fs.readdirSync(new URL('./implementations', import.meta.url)).sort()) {
const filepath = new URL(`./implementations/${name}/index.mjs`, import.meta.url);
@ -31,11 +31,11 @@ fs.readdirSync(new URL('./samples', import.meta.url)).sort().forEach(sample => {
content.string = fs.readFileSync(filepath, 'utf8');
var title = `(${content.string.length} bytes)`;
const title = `(${content.string.length} bytes)`;
function onComplete() { cursor.write('\n'); }
var suite = new Benchmark.Suite(
const suite = new Benchmark.Suite(
title,
{
onStart: () => { console.log('\nSample: %s %s', sample, title); },
@ -68,7 +68,7 @@ fs.readdirSync(new URL('./samples', import.meta.url)).sort().forEach(sample => {
function select(patterns) {
var result = [];
const result = [];
if (!(patterns instanceof Array)) {
patterns = [ patterns ];
@ -91,7 +91,7 @@ function select(patterns) {
function run(files) {
var selected = select(files);
const selected = select(files);
if (selected.length > 0) {
console.log('Selected samples: (%d of %d)', selected.length, SAMPLES.length);

4
benchmark/implementations/commonmark-reference/index.mjs

@ -3,8 +3,8 @@ const require = createRequire(import.meta.url);
const commonmark = require('../../extra/lib/node_modules/commonmark');
var parser = new commonmark.Parser();
var renderer = new commonmark.HtmlRenderer();
const parser = new commonmark.Parser();
const renderer = new commonmark.HtmlRenderer();
export function run(data) {
return renderer.render(parser.parse(data));

4
benchmark/implementations/current-commonmark/index.mjs

@ -1,10 +1,10 @@
import markdownit from '../../../index.mjs';
var md = markdownit('commonmark');
const md = markdownit('commonmark');
// Replace normalizers to more primitive, for more "honest" compare.
// Default ones can cause 1.5x slowdown.
var encode = md.utils.lib.mdurl.encode;
const encode = md.utils.lib.mdurl.encode;
md.normalizeLink = function (url) { return encode(url); };
md.normalizeLinkText = function (str) { return str; };

2
benchmark/implementations/current/index.mjs

@ -1,6 +1,6 @@
import markdownit from '../../../index.mjs';
var md = markdownit({
const md = markdownit({
html: true,
linkify: true,
typographer: true

2
benchmark/implementations/markdown-it-2.2.1-commonmark/index.mjs

@ -3,7 +3,7 @@ const require = createRequire(import.meta.url);
const markdownit = require('../../extra/lib/node_modules/markdown-it');
var md = markdownit('commonmark');
const md = markdownit('commonmark');
export function run(data) {
return md.render(data);

6
benchmark/profile.mjs

@ -4,14 +4,14 @@
import { readFileSync } from 'fs';
import markdownit from '../index.mjs';
var md = markdownit({
const md = markdownit({
html: true,
linkify: false,
typographer: false
});
var data = readFileSync(new URL('../test/fixtures/commonmark/spec.txt', import.meta.url), 'utf8');
const data = readFileSync(new URL('../test/fixtures/commonmark/spec.txt', import.meta.url), 'utf8');
for (var i = 0; i < 20; i++) {
for (let i = 0; i < 20; i++) {
md.render(data);
}

10
bin/markdown-it.mjs

@ -7,7 +7,7 @@ import markdownit from '../index.mjs';
////////////////////////////////////////////////////////////////////////////////
var cli = new argparse.ArgumentParser({
const cli = new argparse.ArgumentParser({
prog: 'markdown-it',
add_help: true
});
@ -48,13 +48,13 @@ cli.add_argument('-o', '--output', {
default: '-'
});
var options = cli.parse_args();
const options = cli.parse_args();
function readFile(filename, encoding, callback) {
if (options.file === '-') {
// read from stdin
var chunks = [];
const chunks = [];
process.stdin.on('data', function (chunk) { chunks.push(chunk); });
@ -70,7 +70,7 @@ function readFile(filename, encoding, callback) {
////////////////////////////////////////////////////////////////////////////////
readFile(options.file, 'utf8', function (err, input) {
var output, md;
let output;
if (err) {
if (err.code === 'ENOENT') {
@ -86,7 +86,7 @@ readFile(options.file, 'utf8', function (err, input) {
process.exit(1);
}
md = markdownit({
const md = markdownit({
html: !options.no_html,
xhtmlOut: false,
typographer: options.typographer,

28
lib/common/html_re.mjs

@ -1,25 +1,25 @@
// Regexps to match html elements
var attr_name = '[a-zA-Z_:][a-zA-Z0-9:._-]*';
const attr_name = '[a-zA-Z_:][a-zA-Z0-9:._-]*';
var unquoted = '[^"\'=<>`\\x00-\\x20]+';
var single_quoted = "'[^']*'";
var double_quoted = '"[^"]*"';
const unquoted = '[^"\'=<>`\\x00-\\x20]+';
const single_quoted = "'[^']*'";
const double_quoted = '"[^"]*"';
var attr_value = '(?:' + unquoted + '|' + single_quoted + '|' + double_quoted + ')';
const attr_value = '(?:' + unquoted + '|' + single_quoted + '|' + double_quoted + ')';
var attribute = '(?:\\s+' + attr_name + '(?:\\s*=\\s*' + attr_value + ')?)';
const attribute = '(?:\\s+' + attr_name + '(?:\\s*=\\s*' + attr_value + ')?)';
var open_tag = '<[A-Za-z][A-Za-z0-9\\-]*' + attribute + '*\\s*\\/?>';
const open_tag = '<[A-Za-z][A-Za-z0-9\\-]*' + attribute + '*\\s*\\/?>';
var close_tag = '<\\/[A-Za-z][A-Za-z0-9\\-]*\\s*>';
var comment = '<!---->|<!--(?:-?[^>-])(?:-?[^-])*-->';
var processing = '<[?][\\s\\S]*?[?]>';
var declaration = '<![A-Z]+\\s+[^>]*>';
var cdata = '<!\\[CDATA\\[[\\s\\S]*?\\]\\]>';
const close_tag = '<\\/[A-Za-z][A-Za-z0-9\\-]*\\s*>';
const comment = '<!---->|<!--(?:-?[^>-])(?:-?[^-])*-->';
const processing = '<[?][\\s\\S]*?[?]>';
const declaration = '<![A-Z]+\\s+[^>]*>';
const cdata = '<!\\[CDATA\\[[\\s\\S]*?\\]\\]>';
var HTML_TAG_RE = new RegExp('^(?:' + open_tag + '|' + close_tag + '|' + comment +
const HTML_TAG_RE = new RegExp('^(?:' + open_tag + '|' + close_tag + '|' + comment +
'|' + processing + '|' + declaration + '|' + cdata + ')');
var HTML_OPEN_CLOSE_TAG_RE = new RegExp('^(?:' + open_tag + '|' + close_tag + ')');
const HTML_OPEN_CLOSE_TAG_RE = new RegExp('^(?:' + open_tag + '|' + close_tag + ')');
export { HTML_TAG_RE, HTML_OPEN_CLOSE_TAG_RE };

30
lib/common/utils.mjs

@ -10,7 +10,7 @@ function _class(obj) { return Object.prototype.toString.call(obj); }
function isString(obj) { return _class(obj) === '[object String]'; }
var _hasOwnProperty = Object.prototype.hasOwnProperty;
const _hasOwnProperty = Object.prototype.hasOwnProperty;
function has(object, key) {
return _hasOwnProperty.call(object, key);
@ -19,7 +19,7 @@ function has(object, key) {
// Merge objects
//
function assign(obj /*from1, from2, from3, ...*/) {
var sources = Array.prototype.slice.call(arguments, 1);
const sources = Array.prototype.slice.call(arguments, 1);
sources.forEach(function (source) {
if (!source) { return; }
@ -65,8 +65,8 @@ function fromCodePoint(c) {
/*eslint no-bitwise:0*/
if (c > 0xffff) {
c -= 0x10000;
var surrogate1 = 0xd800 + (c >> 10),
surrogate2 = 0xdc00 + (c & 0x3ff);
const surrogate1 = 0xd800 + (c >> 10),
surrogate2 = 0xdc00 + (c & 0x3ff);
return String.fromCharCode(surrogate1, surrogate2);
}
@ -74,18 +74,16 @@ function fromCodePoint(c) {
}
var UNESCAPE_MD_RE = /\\([!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~])/g;
var ENTITY_RE = /&([a-z#][a-z0-9]{1,31});/gi;
var UNESCAPE_ALL_RE = new RegExp(UNESCAPE_MD_RE.source + '|' + ENTITY_RE.source, 'gi');
const UNESCAPE_MD_RE = /\\([!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~])/g;
const ENTITY_RE = /&([a-z#][a-z0-9]{1,31});/gi;
const UNESCAPE_ALL_RE = new RegExp(UNESCAPE_MD_RE.source + '|' + ENTITY_RE.source, 'gi');
var DIGITAL_ENTITY_TEST_RE = /^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))$/i;
const DIGITAL_ENTITY_TEST_RE = /^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))$/i;
function replaceEntityPattern(match, name) {
var decoded, code;
if (name.charCodeAt(0) === 0x23/* # */ && DIGITAL_ENTITY_TEST_RE.test(name)) {
code = name[1].toLowerCase() === 'x' ?
const code = name[1].toLowerCase() === 'x' ?
parseInt(name.slice(2), 16) : parseInt(name.slice(1), 10);
if (isValidEntityCode(code)) {
@ -95,7 +93,7 @@ function replaceEntityPattern(match, name) {
return match;
}
decoded = decodeHTML(match);
const decoded = decodeHTML(match);
if (decoded !== match) {
return decoded;
}
@ -125,9 +123,9 @@ function unescapeAll(str) {
////////////////////////////////////////////////////////////////////////////////
var HTML_ESCAPE_TEST_RE = /[&<>"]/;
var HTML_ESCAPE_REPLACE_RE = /[&<>"]/g;
var HTML_REPLACEMENTS = {
const HTML_ESCAPE_TEST_RE = /[&<>"]/;
const HTML_ESCAPE_REPLACE_RE = /[&<>"]/g;
const HTML_REPLACEMENTS = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
@ -147,7 +145,7 @@ function escapeHtml(str) {
////////////////////////////////////////////////////////////////////////////////
var REGEXP_ESCAPE_RE = /[.?*+^$[\]\\(){}|-]/g;
const REGEXP_ESCAPE_RE = /[.?*+^$[\]\\(){}|-]/g;
function escapeRE(str) {
return str.replace(REGEXP_ESCAPE_RE, '\\$&');

19
lib/helpers/parse_link_destination.mjs

@ -4,14 +4,15 @@
import { unescapeAll } from '../common/utils.mjs';
export default function parseLinkDestination(str, start, max) {
var code, level,
pos = start,
result = {
ok: false,
pos: 0,
lines: 0,
str: ''
};
let code;
let pos = start;
const result = {
ok: false,
pos: 0,
lines: 0,
str: ''
};
if (str.charCodeAt(pos) === 0x3C /* < */) {
pos++;
@ -39,7 +40,7 @@ export default function parseLinkDestination(str, start, max) {
// this should be ... } else { ... branch
level = 0;
let level = 0;
while (pos < max) {
code = str.charCodeAt(pos);

10
lib/helpers/parse_link_label.mjs

@ -5,10 +5,10 @@
//
export default function parseLinkLabel(state, start, disableNested) {
var level, found, marker, prevPos,
labelEnd = -1,
max = state.posMax,
oldPos = state.pos;
let level, found, marker, prevPos;
const max = state.posMax;
const oldPos = state.pos;
state.pos = start + 1;
level = 1;
@ -36,6 +36,8 @@ export default function parseLinkLabel(state, start, disableNested) {
}
}
let labelEnd = -1;
if (found) {
labelEnd = state.pos;
}

20
lib/helpers/parse_link_title.mjs

@ -5,16 +5,16 @@ import { unescapeAll } from '../common/utils.mjs';
export default function parseLinkTitle(str, start, max) {
var code,
marker,
lines = 0,
pos = start,
result = {
ok: false,
pos: 0,
lines: 0,
str: ''
};
let code, marker;
let lines = 0;
let pos = start;
const result = {
ok: false,
pos: 0,
lines: 0,
str: ''
};
if (pos >= max) { return result; }

32
lib/index.mjs

@ -14,7 +14,7 @@ import cfg_default from './presets/default.mjs';
import cfg_zero from './presets/zero.mjs';
import cfg_commonmark from './presets/commonmark.mjs';
var config = {
const config = {
default: cfg_default,
zero: cfg_zero,
commonmark: cfg_commonmark
@ -29,12 +29,12 @@ var config = {
// replace it with dummy function and use external sanitizer.
//
var BAD_PROTO_RE = /^(vbscript|javascript|file|data):/;
var GOOD_DATA_RE = /^data:image\/(gif|png|jpeg|webp);/;
const BAD_PROTO_RE = /^(vbscript|javascript|file|data):/;
const GOOD_DATA_RE = /^data:image\/(gif|png|jpeg|webp);/;
function validateLink(url) {
// url should be normalized at this point, and existing entities are decoded
var str = url.trim().toLowerCase();
const str = url.trim().toLowerCase();
return BAD_PROTO_RE.test(str) ? (GOOD_DATA_RE.test(str) ? true : false) : true;
}
@ -42,10 +42,10 @@ function validateLink(url) {
////////////////////////////////////////////////////////////////////////////////
var RECODE_HOSTNAME_FOR = [ 'http:', 'https:', 'mailto:' ];
const RECODE_HOSTNAME_FOR = [ 'http:', 'https:', 'mailto:' ];
function normalizeLink(url) {
var parsed = mdurl.parse(url, true);
const parsed = mdurl.parse(url, true);
if (parsed.hostname) {
// Encode hostnames in urls like:
@ -65,7 +65,7 @@ function normalizeLink(url) {
}
function normalizeLinkText(url) {
var parsed = mdurl.parse(url, true);
const parsed = mdurl.parse(url, true);
if (parsed.hostname) {
// Encode hostnames in urls like:
@ -385,10 +385,10 @@ MarkdownIt.prototype.set = function (options) {
* will give better compatibility with next versions.
**/
MarkdownIt.prototype.configure = function (presets) {
var self = this, presetName;
const self = this;
if (utils.isString(presets)) {
presetName = presets;
const presetName = presets;
presets = config[presetName];
if (!presets) { throw new Error('Wrong `markdown-it` preset "' + presetName + '", check name'); }
}
@ -429,7 +429,7 @@ MarkdownIt.prototype.configure = function (presets) {
* ```
**/
MarkdownIt.prototype.enable = function (list, ignoreInvalid) {
var result = [];
let result = [];
if (!Array.isArray(list)) { list = [ list ]; }
@ -439,7 +439,7 @@ MarkdownIt.prototype.enable = function (list, ignoreInvalid) {
result = result.concat(this.inline.ruler2.enable(list, true));
var missed = list.filter(function (name) { return result.indexOf(name) < 0; });
const missed = list.filter(function (name) { return result.indexOf(name) < 0; });
if (missed.length && !ignoreInvalid) {
throw new Error('MarkdownIt. Failed to enable unknown rule(s): ' + missed);
@ -457,7 +457,7 @@ MarkdownIt.prototype.enable = function (list, ignoreInvalid) {
* The same as [[MarkdownIt.enable]], but turn specified rules off.
**/
MarkdownIt.prototype.disable = function (list, ignoreInvalid) {
var result = [];
let result = [];
if (!Array.isArray(list)) { list = [ list ]; }
@ -467,7 +467,7 @@ MarkdownIt.prototype.disable = function (list, ignoreInvalid) {
result = result.concat(this.inline.ruler2.disable(list, true));
var missed = list.filter(function (name) { return result.indexOf(name) < 0; });
const missed = list.filter(function (name) { return result.indexOf(name) < 0; });
if (missed.length && !ignoreInvalid) {
throw new Error('MarkdownIt. Failed to disable unknown rule(s): ' + missed);
@ -493,7 +493,7 @@ MarkdownIt.prototype.disable = function (list, ignoreInvalid) {
* ```
**/
MarkdownIt.prototype.use = function (plugin /*, params, ... */) {
var args = [ this ].concat(Array.prototype.slice.call(arguments, 1));
const args = [ this ].concat(Array.prototype.slice.call(arguments, 1));
plugin.apply(plugin, args);
return this;
};
@ -519,7 +519,7 @@ MarkdownIt.prototype.parse = function (src, env) {
throw new Error('Input data should be a String');
}
var state = new this.core.State(src, this, env);
const state = new this.core.State(src, this, env);
this.core.process(state);
@ -555,7 +555,7 @@ MarkdownIt.prototype.render = function (src, env) {
* tokens in `children` property. Also updates `env` object.
**/
MarkdownIt.prototype.parseInline = function (src, env) {
var state = new this.core.State(src, this, env);
const state = new this.core.State(src, this, env);
state.inlineMode = true;
this.core.process(state);

24
lib/parser_block.mjs

@ -19,7 +19,7 @@ import r_heading from './rules_block/heading.mjs';
import r_lheading from './rules_block/lheading.mjs';
import r_paragraph from './rules_block/paragraph.mjs';
var _rules = [
const _rules = [
// First 2 params - rule name & source. Secondary array - list of rules,
// which can be terminated by this one.
[ 'table', r_table, [ 'paragraph', 'reference' ] ],
@ -47,7 +47,7 @@ function ParserBlock() {
**/
this.ruler = new Ruler();
for (var i = 0; i < _rules.length; i++) {
for (let i = 0; i < _rules.length; i++) {
this.ruler.push(_rules[i][0], _rules[i][1], { alt: (_rules[i][2] || []).slice() });
}
}
@ -56,12 +56,11 @@ function ParserBlock() {
// Generate tokens for input range
//
ParserBlock.prototype.tokenize = function (state, startLine, endLine) {
var ok, i, prevLine,
rules = this.ruler.getRules(''),
len = rules.length,
line = startLine,
hasEmptyLines = false,
maxNesting = state.md.options.maxNesting;
const rules = this.ruler.getRules('');
const len = rules.length;
const maxNesting = state.md.options.maxNesting;
let line = startLine;
let hasEmptyLines = false;
while (line < endLine) {
state.line = line = state.skipEmptyLines(line);
@ -84,9 +83,10 @@ ParserBlock.prototype.tokenize = function (state, startLine, endLine) {
// - update `state.line`
// - update `state.tokens`
// - return true
prevLine = state.line;
const prevLine = state.line;
let ok = false;
for (i = 0; i < len; i++) {
for (let i = 0; i < len; i++) {
ok = rules[i](state, line, endLine, false);
if (ok) {
if (prevLine >= state.line) {
@ -125,11 +125,9 @@ ParserBlock.prototype.tokenize = function (state, startLine, endLine) {
* Process input string and push block tokens into `outTokens`
**/
ParserBlock.prototype.parse = function (src, md, env, outTokens) {
var state;
if (!src) { return; }
state = new this.State(src, md, env, outTokens);
const state = new this.State(src, md, env, outTokens);
this.tokenize(state, state.line, state.lineMax);
};

10
lib/parser_core.mjs

@ -17,7 +17,7 @@ import r_smartquotes from './rules_core/smartquotes.mjs';
import r_text_join from './rules_core/text_join.mjs';
var _rules = [
const _rules = [
[ 'normalize', r_normalize ],
[ 'block', r_block ],
[ 'inline', r_inline ],
@ -41,7 +41,7 @@ function Core() {
**/
this.ruler = new Ruler();
for (var i = 0; i < _rules.length; i++) {
for (let i = 0; i < _rules.length; i++) {
this.ruler.push(_rules[i][0], _rules[i][1]);
}
}
@ -53,11 +53,9 @@ function Core() {
* Executes core chain rules.
**/
Core.prototype.process = function (state) {
var i, l, rules;
const rules = this.ruler.getRules('');
rules = this.ruler.getRules('');
for (i = 0, l = rules.length; i < l; i++) {
for (let i = 0, l = rules.length; i < l; i++) {
rules[i](state);
}
};

47
lib/parser_inline.mjs

@ -28,7 +28,7 @@ import r_fragments_join from './rules_inline/fragments_join.mjs';
////////////////////////////////////////////////////////////////////////////////
// Parser rules
var _rules = [
const _rules = [
[ 'text', r_text ],
[ 'linkify', r_linkify ],
[ 'newline', r_newline ],
@ -48,7 +48,7 @@ var _rules = [
//
// Don't use this for anything except pairs (plugins working with `balance_pairs`).
//
var _rules2 = [
const _rules2 = [
[ 'balance_pairs', r_balance_pairs ],
[ 'strikethrough', r_strikethrough.postProcess ],
[ 'emphasis', r_emphasis.postProcess ],
@ -62,8 +62,6 @@ var _rules2 = [
* new ParserInline()
**/
function ParserInline() {
var i;
/**
* ParserInline#ruler -> Ruler
*
@ -71,7 +69,7 @@ function ParserInline() {
**/
this.ruler = new Ruler();
for (i = 0; i < _rules.length; i++) {
for (let i = 0; i < _rules.length; i++) {
this.ruler.push(_rules[i][0], _rules[i][1]);
}
@ -83,7 +81,7 @@ function ParserInline() {
**/
this.ruler2 = new Ruler();
for (i = 0; i < _rules2.length; i++) {
for (let i = 0; i < _rules2.length; i++) {
this.ruler2.push(_rules2[i][0], _rules2[i][1]);
}
}
@ -93,11 +91,11 @@ function ParserInline() {
// returns `true` if any rule reported success
//
ParserInline.prototype.skipToken = function (state) {
var ok, i, pos = state.pos,
rules = this.ruler.getRules(''),
len = rules.length,
maxNesting = state.md.options.maxNesting,
cache = state.cache;
const pos = state.pos;
const rules = this.ruler.getRules('');
const len = rules.length;
const maxNesting = state.md.options.maxNesting;
const cache = state.cache;
if (typeof cache[pos] !== 'undefined') {
@ -105,8 +103,10 @@ ParserInline.prototype.skipToken = function (state) {
return;
}
let ok = false;
if (state.level < maxNesting) {
for (i = 0; i < len; i++) {
for (let i = 0; i < len; i++) {
// Increment state.level and decrement it later to limit recursion.
// It's harmless to do here, because no tokens are created. But ideally,
// we'd need a separate private state variable for this purpose.
@ -143,11 +143,10 @@ ParserInline.prototype.skipToken = function (state) {
// Generate tokens for input range
//
ParserInline.prototype.tokenize = function (state) {
var ok, i, prevPos,
rules = this.ruler.getRules(''),
len = rules.length,
end = state.posMax,
maxNesting = state.md.options.maxNesting;
const rules = this.ruler.getRules('');
const len = rules.length;
const end = state.posMax;
const maxNesting = state.md.options.maxNesting;
while (state.pos < end) {
// Try all possible rules.
@ -156,10 +155,11 @@ ParserInline.prototype.tokenize = function (state) {
// - update `state.pos`
// - update `state.tokens`
// - return true
prevPos = state.pos;
const prevPos = state.pos;
let ok = false;
if (state.level < maxNesting) {
for (i = 0; i < len; i++) {
for (let i = 0; i < len; i++) {
ok = rules[i](state, false);
if (ok) {
if (prevPos >= state.pos) { throw new Error("inline rule didn't increment state.pos"); }
@ -188,15 +188,14 @@ ParserInline.prototype.tokenize = function (state) {
* Process input string and push inline tokens into `outTokens`
**/
ParserInline.prototype.parse = function (str, md, env, outTokens) {
var i, rules, len;
var state = new this.State(str, md, env, outTokens);
const state = new this.State(str, md, env, outTokens);
this.tokenize(state);
rules = this.ruler2.getRules('');
len = rules.length;
const rules = this.ruler2.getRules('');
const len = rules.length;
for (i = 0; i < len; i++) {
for (let i = 0; i < len; i++) {
rules[i](state);
}
};

59
lib/renderer.mjs

@ -10,11 +10,11 @@ import { assign, unescapeAll, escapeHtml } from './common/utils.mjs';
////////////////////////////////////////////////////////////////////////////////
var default_rules = {};
const default_rules = {};
default_rules.code_inline = function (tokens, idx, options, env, slf) {
var token = tokens[idx];
const token = tokens[idx];
return '<code' + slf.renderAttrs(token) + '>' +
escapeHtml(token.content) +
@ -23,7 +23,7 @@ default_rules.code_inline = function (tokens, idx, options, env, slf) {
default_rules.code_block = function (tokens, idx, options, env, slf) {
var token = tokens[idx];
const token = tokens[idx];
return '<pre' + slf.renderAttrs(token) + '><code>' +
escapeHtml(tokens[idx].content) +
@ -32,18 +32,18 @@ default_rules.code_block = function (tokens, idx, options, env, slf) {
default_rules.fence = function (tokens, idx, options, env, slf) {
var token = tokens[idx],
info = token.info ? unescapeAll(token.info).trim() : '',
langName = '',
langAttrs = '',
highlighted, i, arr, tmpAttrs, tmpToken;
const token = tokens[idx];
const info = token.info ? unescapeAll(token.info).trim() : '';
let langName = '';
let langAttrs = '';
if (info) {
arr = info.split(/(\s+)/g);
const arr = info.split(/(\s+)/g);
langName = arr[0];
langAttrs = arr.slice(2).join('');
}
let highlighted;
if (options.highlight) {
highlighted = options.highlight(token.content, langName, langAttrs) || escapeHtml(token.content);
} else {
@ -58,8 +58,8 @@ default_rules.fence = function (tokens, idx, options, env, slf) {
// May be, one day we will add .deepClone() for token and simplify this part, but
// now we prefer to keep things local.
if (info) {
i = token.attrIndex('class');
tmpAttrs = token.attrs ? token.attrs.slice() : [];
const i = token.attrIndex('class');
const tmpAttrs = token.attrs ? token.attrs.slice() : [];
if (i < 0) {
tmpAttrs.push([ 'class', options.langPrefix + langName ]);
@ -69,7 +69,7 @@ default_rules.fence = function (tokens, idx, options, env, slf) {
}
// Fake token just to render attributes
tmpToken = {
const tmpToken = {
attrs: tmpAttrs
};
@ -86,7 +86,7 @@ default_rules.fence = function (tokens, idx, options, env, slf) {
default_rules.image = function (tokens, idx, options, env, slf) {
var token = tokens[idx];
const token = tokens[idx];
// "alt" attr MUST be set, even if empty. Because it's mandatory and
// should be placed on proper position for tests.
@ -166,7 +166,7 @@ function Renderer() {
* Render token attributes to string.
**/
Renderer.prototype.renderAttrs = function renderAttrs(token) {
var i, l, result;
let i, l, result;
if (!token.attrs) { return ''; }
@ -190,10 +190,8 @@ Renderer.prototype.renderAttrs = function renderAttrs(token) {
* in [[Renderer#rules]].
**/
Renderer.prototype.renderToken = function renderToken(tokens, idx, options) {
var nextToken,
result = '',
needLf = false,
token = tokens[idx];
const token = tokens[idx];
let result = '';
// Tight list paragraphs
if (token.hidden) {
@ -223,12 +221,13 @@ Renderer.prototype.renderToken = function renderToken(tokens, idx, options) {
}
// Check if we need to add a newline after this tag
let needLf = false;
if (token.block) {
needLf = true;
if (token.nesting === 1) {
if (idx + 1 < tokens.length) {
nextToken = tokens[idx + 1];
const nextToken = tokens[idx + 1];
if (nextToken.type === 'inline' || nextToken.hidden) {
// Block-level tag containing an inline tag.
@ -259,12 +258,11 @@ Renderer.prototype.renderToken = function renderToken(tokens, idx, options) {
* The same as [[Renderer.render]], but for single token of `inline` type.
**/
Renderer.prototype.renderInline = function (tokens, options, env) {
var type,
result = '',
rules = this.rules;
let result = '';
const rules = this.rules;
for (var i = 0, len = tokens.length; i < len; i++) {
type = tokens[i].type;
for (let i = 0, len = tokens.length; i < len; i++) {
const type = tokens[i].type;
if (typeof rules[type] !== 'undefined') {
result += rules[type](tokens, i, options, env, this);
@ -288,9 +286,9 @@ Renderer.prototype.renderInline = function (tokens, options, env) {
* instead of simple escaping.
**/
Renderer.prototype.renderInlineAsText = function (tokens, options, env) {
var result = '';
let result = '';
for (var i = 0, len = tokens.length; i < len; i++) {
for (let i = 0, len = tokens.length; i < len; i++) {
if (tokens[i].type === 'text') {
result += tokens[i].content;
} else if (tokens[i].type === 'image') {
@ -314,12 +312,11 @@ Renderer.prototype.renderInlineAsText = function (tokens, options, env) {
* this method directly.
**/
Renderer.prototype.render = function (tokens, options, env) {
var i, len, type,
result = '',
rules = this.rules;
let result = '';
const rules = this.rules;
for (i = 0, len = tokens.length; i < len; i++) {
type = tokens[i].type;
for (let i = 0, len = tokens.length; i < len; i++) {
const type = tokens[i].type;
if (type === 'inline') {
result += this.renderInline(tokens[i].children, options, env);

28
lib/ruler.mjs

@ -47,7 +47,7 @@ function Ruler() {
// Find rule index by name
//
Ruler.prototype.__find__ = function (name) {
for (var i = 0; i < this.__rules__.length; i++) {
for (let i = 0; i < this.__rules__.length; i++) {
if (this.__rules__[i].name === name) {
return i;
}
@ -59,8 +59,8 @@ Ruler.prototype.__find__ = function (name) {
// Build rules lookup cache
//
Ruler.prototype.__compile__ = function () {
var self = this;
var chains = [ '' ];
const self = this;
const chains = [ '' ];
// collect unique names
self.__rules__.forEach(function (rule) {
@ -114,8 +114,8 @@ Ruler.prototype.__compile__ = function () {
* ```
**/
Ruler.prototype.at = function (name, fn, options) {
var index = this.__find__(name);
var opt = options || {};
const index = this.__find__(name);
const opt = options || {};
if (index === -1) { throw new Error('Parser rule not found: ' + name); }
@ -150,8 +150,8 @@ Ruler.prototype.at = function (name, fn, options) {
* ```
**/
Ruler.prototype.before = function (beforeName, ruleName, fn, options) {
var index = this.__find__(beforeName);
var opt = options || {};
const index = this.__find__(beforeName);
const opt = options || {};
if (index === -1) { throw new Error('Parser rule not found: ' + beforeName); }
@ -191,8 +191,8 @@ Ruler.prototype.before = function (beforeName, ruleName, fn, options) {
* ```
**/
Ruler.prototype.after = function (afterName, ruleName, fn, options) {
var index = this.__find__(afterName);
var opt = options || {};
const index = this.__find__(afterName);
const opt = options || {};
if (index === -1) { throw new Error('Parser rule not found: ' + afterName); }
@ -230,7 +230,7 @@ Ruler.prototype.after = function (afterName, ruleName, fn, options) {
* ```
**/
Ruler.prototype.push = function (ruleName, fn, options) {
var opt = options || {};
const opt = options || {};
this.__rules__.push({
name: ruleName,
@ -258,11 +258,11 @@ Ruler.prototype.push = function (ruleName, fn, options) {
Ruler.prototype.enable = function (list, ignoreInvalid) {
if (!Array.isArray(list)) { list = [ list ]; }
var result = [];
const result = [];
// Search by name and enable
list.forEach(function (name) {
var idx = this.__find__(name);
const idx = this.__find__(name);
if (idx < 0) {
if (ignoreInvalid) { return; }
@ -311,11 +311,11 @@ Ruler.prototype.enableOnly = function (list, ignoreInvalid) {
Ruler.prototype.disable = function (list, ignoreInvalid) {
if (!Array.isArray(list)) { list = [ list ]; }
var result = [];
const result = [];
// Search by name and disable
list.forEach(function (name) {
var idx = this.__find__(name);
const idx = this.__find__(name);
if (idx < 0) {
if (ignoreInvalid) { return; }

70
lib/rules_block/blockquote.mjs

@ -3,29 +3,10 @@
import { isSpace } from '../common/utils.mjs';
export default function blockquote(state, startLine, endLine, silent) {
var adjustTab,
ch,
i,
initial,
l,
lastLineEmpty,
lines,
nextLine,
offset,
oldBMarks,
oldBSCount,
oldIndent,
oldParentType,
oldSCount,
oldTShift,
spaceAfterMarker,
terminate,
terminatorRules,
token,
isOutdented,
oldLineMax = state.lineMax,
pos = state.bMarks[startLine] + state.tShift[startLine],
max = state.eMarks[startLine];
let pos = state.bMarks[startLine] + state.tShift[startLine];
let max = state.eMarks[startLine];
const oldLineMax = state.lineMax;
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
@ -37,15 +18,17 @@ export default function blockquote(state, startLine, endLine, silent) {
// so no point trying to find the end of it in silent mode
if (silent) { return true; }
oldBMarks = [];
oldBSCount = [];
oldSCount = [];
oldTShift = [];
const oldBMarks = [];
const oldBSCount = [];
const oldSCount = [];
const oldTShift = [];
terminatorRules = state.md.block.ruler.getRules('blockquote');
const terminatorRules = state.md.block.ruler.getRules('blockquote');
oldParentType = state.parentType;
const oldParentType = state.parentType;
state.parentType = 'blockquote';
let lastLineEmpty = false;
let nextLine;
// Search the end of the block
//
@ -74,7 +57,7 @@ export default function blockquote(state, startLine, endLine, silent) {
// > current blockquote
// 2. checking this line
// ```
isOutdented = state.sCount[nextLine] < state.blkIndent;
const isOutdented = state.sCount[nextLine] < state.blkIndent;
pos = state.bMarks[nextLine] + state.tShift[nextLine];
max = state.eMarks[nextLine];
@ -88,7 +71,9 @@ export default function blockquote(state, startLine, endLine, silent) {
// This line is inside the blockquote.
// set offset past spaces and ">"
initial = state.sCount[nextLine] + 1;
let initial = state.sCount[nextLine] + 1;
let spaceAfterMarker;
let adjustTab;
// skip one optional space after '>'
if (state.src.charCodeAt(pos) === 0x20 /* space */) {
@ -117,12 +102,12 @@ export default function blockquote(state, startLine, endLine, silent) {
spaceAfterMarker = false;
}
offset = initial;
let offset = initial;
oldBMarks.push(state.bMarks[nextLine]);
state.bMarks[nextLine] = pos;
while (pos < max) {
ch = state.src.charCodeAt(pos);
const ch = state.src.charCodeAt(pos);
if (isSpace(ch)) {
if (ch === 0x09) {
@ -154,8 +139,8 @@ export default function blockquote(state, startLine, endLine, silent) {
if (lastLineEmpty) { break; }
// Case 3: another tag found.
terminate = false;
for (i = 0, l = terminatorRules.length; i < l; i++) {
let terminate = false;
for (let i = 0, l = terminatorRules.length; i < l; i++) {
if (terminatorRules[i](state, nextLine, endLine, true)) {
terminate = true;
break;
@ -193,17 +178,18 @@ export default function blockquote(state, startLine, endLine, silent) {
state.sCount[nextLine] = -1;
}
oldIndent = state.blkIndent;
const oldIndent = state.blkIndent;
state.blkIndent = 0;
token = state.push('blockquote_open', 'blockquote', 1);
token.markup = '>';
token.map = lines = [ startLine, 0 ];
const token_o = state.push('blockquote_open', 'blockquote', 1);
token_o.markup = '>';
const lines = [ startLine, 0 ];
token_o.map = lines;
state.md.block.tokenize(state, startLine, nextLine);
token = state.push('blockquote_close', 'blockquote', -1);
token.markup = '>';
const token_c = state.push('blockquote_close', 'blockquote', -1);
token_c.markup = '>';
state.lineMax = oldLineMax;
state.parentType = oldParentType;
@ -211,7 +197,7 @@ export default function blockquote(state, startLine, endLine, silent) {
// Restore original tShift; this might not be necessary since the parser
// has already been here, but just to make sure we can do that.
for (i = 0; i < oldTShift.length; i++) {
for (let i = 0; i < oldTShift.length; i++) {
state.bMarks[i + startLine] = oldBMarks[i];
state.tShift[i + startLine] = oldTShift[i];
state.sCount[i + startLine] = oldSCount[i];

7
lib/rules_block/code.mjs

@ -1,11 +1,10 @@
// Code block (4 spaces padded)
export default function code(state, startLine, endLine/*, silent*/) {
var nextLine, last, token;
if (state.sCount[startLine] - state.blkIndent < 4) { return false; }
last = nextLine = startLine + 1;
let nextLine = startLine + 1;
let last = nextLine;
while (nextLine < endLine) {
if (state.isEmpty(nextLine)) {
@ -23,7 +22,7 @@ export default function code(state, startLine, endLine/*, silent*/) {
state.line = last;
token = state.push('code_block', 'code', 0);
const token = state.push('code_block', 'code', 0);
token.content = state.getLines(startLine, last, 4 + state.blkIndent, false) + '\n';
token.map = [ startLine, state.line ];

21
lib/rules_block/fence.mjs

@ -1,32 +1,30 @@
// fences (``` lang, ~~~ lang)
export default function fence(state, startLine, endLine, silent) {
var marker, len, params, nextLine, mem, token, markup,
haveEndMarker = false,
pos = state.bMarks[startLine] + state.tShift[startLine],
max = state.eMarks[startLine];
let pos = state.bMarks[startLine] + state.tShift[startLine];
let max = state.eMarks[startLine];
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
if (pos + 3 > max) { return false; }
marker = state.src.charCodeAt(pos);
const marker = state.src.charCodeAt(pos);
if (marker !== 0x7E/* ~ */ && marker !== 0x60 /* ` */) {
return false;
}
// scan marker length
mem = pos;
let mem = pos;
pos = state.skipChars(pos, marker);
len = pos - mem;
let len = pos - mem;
if (len < 3) { return false; }
markup = state.src.slice(mem, pos);
params = state.src.slice(pos, max);
const markup = state.src.slice(mem, pos);
const params = state.src.slice(pos, max);
if (marker === 0x60 /* ` */) {
if (params.indexOf(String.fromCharCode(marker)) >= 0) {
@ -38,7 +36,8 @@ export default function fence(state, startLine, endLine, silent) {
if (silent) { return true; }
// search end of block
nextLine = startLine;
let nextLine = startLine;
let haveEndMarker = false;
for (;;) {
nextLine++;
@ -85,7 +84,7 @@ export default function fence(state, startLine, endLine, silent) {
state.line = nextLine + (haveEndMarker ? 1 : 0);
token = state.push('fence', 'code', 0);
const token = state.push('fence', 'code', 0);
token.info = params;
token.content = state.getLines(startLine + 1, nextLine, len, true);
token.markup = markup;

29
lib/rules_block/heading.mjs

@ -3,19 +3,18 @@
import { isSpace } from '../common/utils.mjs';
export default function heading(state, startLine, endLine, silent) {
var ch, level, tmp, token,
pos = state.bMarks[startLine] + state.tShift[startLine],
max = state.eMarks[startLine];
let pos = state.bMarks[startLine] + state.tShift[startLine];
let max = state.eMarks[startLine];
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
ch = state.src.charCodeAt(pos);
let ch = state.src.charCodeAt(pos);
if (ch !== 0x23/* # */ || pos >= max) { return false; }
// count heading level
level = 1;
let level = 1;
ch = state.src.charCodeAt(++pos);
while (ch === 0x23/* # */ && pos < max && level <= 6) {
level++;
@ -29,24 +28,24 @@ export default function heading(state, startLine, endLine, silent) {
// Let's cut tails like ' ### ' from the end of string
max = state.skipSpacesBack(max, pos);
tmp = state.skipCharsBack(max, 0x23, pos); // #
const tmp = state.skipCharsBack(max, 0x23, pos); // #
if (tmp > pos && isSpace(state.src.charCodeAt(tmp - 1))) {
max = tmp;
}
state.line = startLine + 1;
token = state.push('heading_open', 'h' + String(level), 1);
token.markup = '########'.slice(0, level);
token.map = [ startLine, state.line ];
const token_o = state.push('heading_open', 'h' + String(level), 1);
token_o.markup = '########'.slice(0, level);
token_o.map = [ startLine, state.line ];
token = state.push('inline', '', 0);
token.content = state.src.slice(pos, max).trim();
token.map = [ startLine, state.line ];
token.children = [];
const token_i = state.push('inline', '', 0);
token_i.content = state.src.slice(pos, max).trim();
token_i.map = [ startLine, state.line ];
token_i.children = [];
token = state.push('heading_close', 'h' + String(level), -1);
token.markup = '########'.slice(0, level);
const token_c = state.push('heading_close', 'h' + String(level), -1);
token_c.markup = '########'.slice(0, level);
return true;
}

14
lib/rules_block/hr.mjs

@ -3,14 +3,12 @@
import { isSpace } from '../common/utils.mjs';
export default function hr(state, startLine, endLine, silent) {
var marker, cnt, ch, token,
pos = state.bMarks[startLine] + state.tShift[startLine],
max = state.eMarks[startLine];
const max = state.eMarks[startLine];
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
marker = state.src.charCodeAt(pos++);
let pos = state.bMarks[startLine] + state.tShift[startLine];
const marker = state.src.charCodeAt(pos++);
// Check hr marker
if (marker !== 0x2A/* * */ &&
@ -21,9 +19,9 @@ export default function hr(state, startLine, endLine, silent) {
// markers can be mixed with spaces, but there should be at least 3 of them
cnt = 1;
let cnt = 1;
while (pos < max) {
ch = state.src.charCodeAt(pos++);
const ch = state.src.charCodeAt(pos++);
if (ch !== marker && !isSpace(ch)) { return false; }
if (ch === marker) { cnt++; }
}
@ -34,7 +32,7 @@ export default function hr(state, startLine, endLine, silent) {
state.line = startLine + 1;
token = state.push('hr', 'hr', 0);
const token = state.push('hr', 'hr', 0);
token.map = [ startLine, state.line ];
token.markup = Array(cnt + 1).join(String.fromCharCode(marker));

17
lib/rules_block/html_block.mjs

@ -6,7 +6,7 @@ import { HTML_OPEN_CLOSE_TAG_RE } from '../common/html_re.mjs';
// An array of opening and corresponding closing sequences for html tags,
// last argument defines whether it can terminate a paragraph or not
//
var HTML_SEQUENCES = [
const HTML_SEQUENCES = [
[ /^<(script|pre|style|textarea)(?=(\s|>|$))/i, /<\/(script|pre|style|textarea)>/i, true ],
[ /^<!--/, /-->/, true ],
[ /^<\?/, /\?>/, true ],
@ -18,9 +18,8 @@ var HTML_SEQUENCES = [
export default function html_block(state, startLine, endLine, silent) {
var i, nextLine, token, lineText,
pos = state.bMarks[startLine] + state.tShift[startLine],
max = state.eMarks[startLine];
let pos = state.bMarks[startLine] + state.tShift[startLine];
let max = state.eMarks[startLine];
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
@ -29,12 +28,12 @@ export default function html_block(state, startLine, endLine, silent) {
if (state.src.charCodeAt(pos) !== 0x3C/* < */) { return false; }
lineText = state.src.slice(pos, max);
let lineText = state.src.slice(pos, max);
for (i = 0; i < HTML_SEQUENCES.length; i++) {
let i = 0;
for (; i < HTML_SEQUENCES.length; i++) {
if (HTML_SEQUENCES[i][0].test(lineText)) { break; }
}
if (i === HTML_SEQUENCES.length) { return false; }
if (silent) {
@ -42,7 +41,7 @@ export default function html_block(state, startLine, endLine, silent) {
return HTML_SEQUENCES[i][2];
}
nextLine = startLine + 1;
let nextLine = startLine + 1;
// If we are here - we detected HTML block.
// Let's roll down till block end.
@ -63,7 +62,7 @@ export default function html_block(state, startLine, endLine, silent) {
state.line = nextLine;
token = state.push('html_block', '', 0);
const token = state.push('html_block', '', 0);
token.map = [ startLine, nextLine ];
token.content = state.getLines(startLine, nextLine, state.blkIndent, true);

38
lib/rules_block/lheading.mjs

@ -1,17 +1,19 @@
// lheading (---, ===)
export default function lheading(state, startLine, endLine/*, silent*/) {
var content, terminate, i, l, token, pos, max, level, marker,
nextLine = startLine + 1, oldParentType,
terminatorRules = state.md.block.ruler.getRules('paragraph');
const terminatorRules = state.md.block.ruler.getRules('paragraph');
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
oldParentType = state.parentType;
const oldParentType = state.parentType;
state.parentType = 'paragraph'; // use paragraph to match terminatorRules
// jump line-by-line until empty one or EOF
let level = 0;
let marker;
let nextLine = startLine + 1;
for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
// this would be a code block normally, but after paragraph
// it's considered a lazy continuation regardless of what's there
@ -21,8 +23,8 @@ export default function lheading(state, startLine, endLine/*, silent*/) {
// Check for underline in setext header
//
if (state.sCount[nextLine] >= state.blkIndent) {
pos = state.bMarks[nextLine] + state.tShift[nextLine];
max = state.eMarks[nextLine];
let pos = state.bMarks[nextLine] + state.tShift[nextLine];
const max = state.eMarks[nextLine];
if (pos < max) {
marker = state.src.charCodeAt(pos);
@ -43,8 +45,8 @@ export default function lheading(state, startLine, endLine/*, silent*/) {
if (state.sCount[nextLine] < 0) { continue; }
// Some tags can terminate paragraph without empty line.
terminate = false;
for (i = 0, l = terminatorRules.length; i < l; i++) {
let terminate = false;
for (let i = 0, l = terminatorRules.length; i < l; i++) {
if (terminatorRules[i](state, nextLine, endLine, true)) {
terminate = true;
break;
@ -58,21 +60,21 @@ export default function lheading(state, startLine, endLine/*, silent*/) {
return false;
}
content = state.getLines(startLine, nextLine, state.blkIndent, false).trim();
const content = state.getLines(startLine, nextLine, state.blkIndent, false).trim();
state.line = nextLine + 1;
token = state.push('heading_open', 'h' + String(level), 1);
token.markup = String.fromCharCode(marker);
token.map = [ startLine, state.line ];
const token_o = state.push('heading_open', 'h' + String(level), 1);
token_o.markup = String.fromCharCode(marker);
token_o.map = [ startLine, state.line ];
token = state.push('inline', '', 0);
token.content = content;
token.map = [ startLine, state.line - 1 ];
token.children = [];
const token_i = state.push('inline', '', 0);
token_i.content = content;
token_i.map = [ startLine, state.line - 1 ];
token_i.children = [];
token = state.push('heading_close', 'h' + String(level), -1);
token.markup = String.fromCharCode(marker);
const token_c = state.push('heading_close', 'h' + String(level), -1);
token_c.markup = String.fromCharCode(marker);
state.parentType = oldParentType;

101
lib/rules_block/list.mjs

@ -5,12 +5,10 @@ import { isSpace } from '../common/utils.mjs';
// Search `[-+*][\n ]`, returns next pos after marker on success
// or -1 on fail.
function skipBulletListMarker(state, startLine) {
var marker, pos, max, ch;
const max = state.eMarks[startLine];
let pos = state.bMarks[startLine] + state.tShift[startLine];
pos = state.bMarks[startLine] + state.tShift[startLine];
max = state.eMarks[startLine];
marker = state.src.charCodeAt(pos++);
const marker = state.src.charCodeAt(pos++);
// Check bullet
if (marker !== 0x2A/* * */ &&
marker !== 0x2D/* - */ &&
@ -19,7 +17,7 @@ function skipBulletListMarker(state, startLine) {
}
if (pos < max) {
ch = state.src.charCodeAt(pos);
const ch = state.src.charCodeAt(pos);
if (!isSpace(ch)) {
// " -test " - is not a list item
@ -33,15 +31,14 @@ function skipBulletListMarker(state, startLine) {
// Search `\d+[.)][\n ]`, returns next pos after marker on success
// or -1 on fail.
function skipOrderedListMarker(state, startLine) {
var ch,
start = state.bMarks[startLine] + state.tShift[startLine],
pos = start,
max = state.eMarks[startLine];
const start = state.bMarks[startLine] + state.tShift[startLine];
const max = state.eMarks[startLine];
let pos = start;
// List marker should have at least 2 chars (digit + dot)
if (pos + 1 >= max) { return -1; }
ch = state.src.charCodeAt(pos++);
let ch = state.src.charCodeAt(pos++);
if (ch < 0x30/* 0 */ || ch > 0x39/* 9 */) { return -1; }
@ -68,7 +65,6 @@ function skipOrderedListMarker(state, startLine) {
return -1;
}
if (pos < max) {
ch = state.src.charCodeAt(pos);
@ -81,10 +77,9 @@ function skipOrderedListMarker(state, startLine) {
}
function markTightParagraphs(state, idx) {
var i, l,
level = state.level + 2;
const level = state.level + 2;
for (i = idx + 2, l = state.tokens.length - 2; i < l; i++) {
for (let i = idx + 2, l = state.tokens.length - 2; i < l; i++) {
if (state.tokens[i].level === level && state.tokens[i].type === 'paragraph_open') {
state.tokens[i + 2].hidden = true;
state.tokens[i].hidden = true;
@ -95,36 +90,13 @@ function markTightParagraphs(state, idx) {
export default function list(state, startLine, endLine, silent) {
var ch,
contentStart,
i,
indent,
indentAfterMarker,
initial,
isOrdered,
itemLines,
l,
listLines,
listTokIdx,
markerCharCode,
markerValue,
max,
offset,
oldListIndent,
oldParentType,
oldSCount,
oldTShift,
oldTight,
let max,
pos,
posAfterMarker,
prevEmptyEnd,
start,
terminate,
terminatorRules,
token,
nextLine = startLine,
isTerminatingParagraph = false,
tight = true;
token;
let nextLine = startLine;
let tight = true;
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[nextLine] - state.blkIndent >= 4) { return false; }
@ -141,6 +113,8 @@ export default function list(state, startLine, endLine, silent) {
return false;
}
let isTerminatingParagraph = false;
// limit conditions when list can interrupt
// a paragraph (validation mode only)
if (silent && state.parentType === 'paragraph') {
@ -155,6 +129,9 @@ export default function list(state, startLine, endLine, silent) {
}
// Detect list type and position after marker
let isOrdered;
let markerValue;
let posAfterMarker;
if ((posAfterMarker = skipOrderedListMarker(state, nextLine)) >= 0) {
isOrdered = true;
start = state.bMarks[nextLine] + state.tShift[nextLine];
@ -181,10 +158,10 @@ export default function list(state, startLine, endLine, silent) {
if (silent) { return true; }
// We should terminate list on style change. Remember first one to compare.
markerCharCode = state.src.charCodeAt(posAfterMarker - 1);
const markerCharCode = state.src.charCodeAt(posAfterMarker - 1);
// Start list
listTokIdx = state.tokens.length;
const listTokIdx = state.tokens.length;
if (isOrdered) {
token = state.push('ordered_list_open', 'ol', 1);
@ -196,27 +173,29 @@ export default function list(state, startLine, endLine, silent) {
token = state.push('bullet_list_open', 'ul', 1);
}
token.map = listLines = [ nextLine, 0 ];
const listLines = [ nextLine, 0 ];
token.map = listLines;
token.markup = String.fromCharCode(markerCharCode);
//
// Iterate list items
//
prevEmptyEnd = false;
terminatorRules = state.md.block.ruler.getRules('list');
let prevEmptyEnd = false;
const terminatorRules = state.md.block.ruler.getRules('list');
oldParentType = state.parentType;
const oldParentType = state.parentType;
state.parentType = 'list';
while (nextLine < endLine) {
pos = posAfterMarker;
max = state.eMarks[nextLine];
initial = offset = state.sCount[nextLine] + posAfterMarker - (state.bMarks[nextLine] + state.tShift[nextLine]);
const initial = state.sCount[nextLine] + posAfterMarker - (state.bMarks[nextLine] + state.tShift[nextLine]);
let offset = initial;
while (pos < max) {
ch = state.src.charCodeAt(pos);
const ch = state.src.charCodeAt(pos);
if (ch === 0x09) {
offset += 4 - (offset + state.bsCount[nextLine]) % 4;
@ -229,7 +208,8 @@ export default function list(state, startLine, endLine, silent) {
pos++;
}
contentStart = pos;
const contentStart = pos;
let indentAfterMarker;
if (contentStart >= max) {
// trimming space in "- \n 3" case, indent is 1 here
@ -244,26 +224,27 @@ export default function list(state, startLine, endLine, silent) {
// " - test"
// ^^^^^ - calculating total length of this thing
indent = initial + indentAfterMarker;
const indent = initial + indentAfterMarker;
// Run subparser & write tokens
token = state.push('list_item_open', 'li', 1);
token.markup = String.fromCharCode(markerCharCode);
token.map = itemLines = [ nextLine, 0 ];
const itemLines = [ nextLine, 0 ];
token.map = itemLines;
if (isOrdered) {
token.info = state.src.slice(start, posAfterMarker - 1);
}
// change current state, then restore it after parser subcall
oldTight = state.tight;
oldTShift = state.tShift[nextLine];
oldSCount = state.sCount[nextLine];
const oldTight = state.tight;
const oldTShift = state.tShift[nextLine];
const oldSCount = state.sCount[nextLine];
// - example list
// ^ listIndent position will be here
// ^ blkIndent position will be here
//
oldListIndent = state.listIndent;
const oldListIndent = state.listIndent;
state.listIndent = state.blkIndent;
state.blkIndent = indent;
@ -315,8 +296,8 @@ export default function list(state, startLine, endLine, silent) {
if (state.sCount[nextLine] - state.blkIndent >= 4) { break; }
// fail if terminating block found
terminate = false;
for (i = 0, l = terminatorRules.length; i < l; i++) {
let terminate = false;
for (let i = 0, l = terminatorRules.length; i < l; i++) {
if (terminatorRules[i](state, nextLine, endLine, true)) {
terminate = true;
break;

28
lib/rules_block/paragraph.mjs

@ -1,11 +1,9 @@
// Paragraph
export default function paragraph(state, startLine, endLine) {
var content, terminate, i, l, token, oldParentType,
nextLine = startLine + 1,
terminatorRules = state.md.block.ruler.getRules('paragraph');
oldParentType = state.parentType;
const terminatorRules = state.md.block.ruler.getRules('paragraph');
const oldParentType = state.parentType;
let nextLine = startLine + 1;
state.parentType = 'paragraph';
// jump line-by-line until empty one or EOF
@ -18,8 +16,8 @@ export default function paragraph(state, startLine, endLine) {
if (state.sCount[nextLine] < 0) { continue; }
// Some tags can terminate paragraph without empty line.
terminate = false;
for (i = 0, l = terminatorRules.length; i < l; i++) {
let terminate = false;
for (let i = 0, l = terminatorRules.length; i < l; i++) {
if (terminatorRules[i](state, nextLine, endLine, true)) {
terminate = true;
break;
@ -28,19 +26,19 @@ export default function paragraph(state, startLine, endLine) {
if (terminate) { break; }
}
content = state.getLines(startLine, nextLine, state.blkIndent, false).trim();
const content = state.getLines(startLine, nextLine, state.blkIndent, false).trim();
state.line = nextLine;
token = state.push('paragraph_open', 'p', 1);
token.map = [ startLine, state.line ];
const token_o = state.push('paragraph_open', 'p', 1);
token_o.map = [ startLine, state.line ];
token = state.push('inline', '', 0);
token.content = content;
token.map = [ startLine, state.line ];
token.children = [];
const token_i = state.push('inline', '', 0);
token_i.content = content;
token_i.map = [ startLine, state.line ];
token_i.children = [];
token = state.push('paragraph_close', 'p', -1);
state.push('paragraph_close', 'p', -1);
state.parentType = oldParentType;

78
lib/rules_block/reference.mjs

@ -1,26 +1,11 @@
import { isSpace, normalizeReference } from '../common/utils.mjs';
export default function reference(state, startLine, _endLine, silent) {
var ch,
destEndPos,
destEndLineNo,
endLine,
href,
i,
l,
label,
labelEnd,
oldParentType,
res,
start,
str,
terminate,
terminatorRules,
title,
lines = 0,
pos = state.bMarks[startLine] + state.tShift[startLine],
max = state.eMarks[startLine],
nextLine = startLine + 1;
let lines = 0;
let pos = state.bMarks[startLine] + state.tShift[startLine];
let max = state.eMarks[startLine];
let nextLine = startLine + 1;
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
@ -38,12 +23,12 @@ export default function reference(state, startLine, _endLine, silent) {
}
}
endLine = state.lineMax;
const endLine = state.lineMax;
// jump line-by-line until empty one or EOF
terminatorRules = state.md.block.ruler.getRules('reference');
const terminatorRules = state.md.block.ruler.getRules('reference');
oldParentType = state.parentType;
const oldParentType = state.parentType;
state.parentType = 'reference';
for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
@ -55,8 +40,8 @@ export default function reference(state, startLine, _endLine, silent) {
if (state.sCount[nextLine] < 0) { continue; }
// Some tags can terminate paragraph without empty line.
terminate = false;
for (i = 0, l = terminatorRules.length; i < l; i++) {
let terminate = false;
for (let i = 0, l = terminatorRules.length; i < l; i++) {
if (terminatorRules[i](state, nextLine, endLine, true)) {
terminate = true;
break;
@ -65,11 +50,12 @@ export default function reference(state, startLine, _endLine, silent) {
if (terminate) { break; }
}
str = state.getLines(startLine, nextLine, state.blkIndent, false).trim();
const str = state.getLines(startLine, nextLine, state.blkIndent, false).trim();
max = str.length;
let labelEnd = -1;
for (pos = 1; pos < max; pos++) {
ch = str.charCodeAt(pos);
const ch = str.charCodeAt(pos);
if (ch === 0x5B /* [ */) {
return false;
} else if (ch === 0x5D /* ] */) {
@ -90,7 +76,7 @@ export default function reference(state, startLine, _endLine, silent) {
// [label]: destination 'title'
// ^^^ skip optional whitespace here
for (pos = labelEnd + 2; pos < max; pos++) {
ch = str.charCodeAt(pos);
const ch = str.charCodeAt(pos);
if (ch === 0x0A) {
lines++;
} else if (isSpace(ch)) {
@ -102,24 +88,24 @@ export default function reference(state, startLine, _endLine, silent) {
// [label]: destination 'title'
// ^^^^^^^^^^^ parse this
res = state.md.helpers.parseLinkDestination(str, pos, max);
if (!res.ok) { return false; }
const destRes = state.md.helpers.parseLinkDestination(str, pos, max);
if (!destRes.ok) { return false; }
href = state.md.normalizeLink(res.str);
const href = state.md.normalizeLink(destRes.str);
if (!state.md.validateLink(href)) { return false; }
pos = res.pos;
lines += res.lines;
pos = destRes.pos;
lines += destRes.lines;
// save cursor state, we could require to rollback later
destEndPos = pos;
destEndLineNo = lines;
const destEndPos = pos;
const destEndLineNo = lines;
// [label]: destination 'title'
// ^^^ skipping those spaces
start = pos;
const start = pos;
for (; pos < max; pos++) {
ch = str.charCodeAt(pos);
const ch = str.charCodeAt(pos);
if (ch === 0x0A) {
lines++;
} else if (isSpace(ch)) {
@ -131,11 +117,13 @@ export default function reference(state, startLine, _endLine, silent) {
// [label]: destination 'title'
// ^^^^^^^ parse this
res = state.md.helpers.parseLinkTitle(str, pos, max);
if (pos < max && start !== pos && res.ok) {
title = res.str;
pos = res.pos;
lines += res.lines;
const titleRes = state.md.helpers.parseLinkTitle(str, pos, max);
let title;
if (pos < max && start !== pos && titleRes.ok) {
title = titleRes.str;
pos = titleRes.pos;
lines += titleRes.lines;
} else {
title = '';
pos = destEndPos;
@ -144,7 +132,7 @@ export default function reference(state, startLine, _endLine, silent) {
// skip trailing spaces until the rest of the line
while (pos < max) {
ch = str.charCodeAt(pos);
const ch = str.charCodeAt(pos);
if (!isSpace(ch)) { break; }
pos++;
}
@ -157,7 +145,7 @@ export default function reference(state, startLine, _endLine, silent) {
pos = destEndPos;
lines = destEndLineNo;
while (pos < max) {
ch = str.charCodeAt(pos);
const ch = str.charCodeAt(pos);
if (!isSpace(ch)) { break; }
pos++;
}
@ -169,7 +157,7 @@ export default function reference(state, startLine, _endLine, silent) {
return false;
}
label = normalizeReference(str.slice(1, labelEnd));
const label = normalizeReference(str.slice(1, labelEnd));
if (!label) {
// CommonMark 0.20 disallows empty labels
return false;

36
lib/rules_block/state_block.mjs

@ -5,8 +5,6 @@ import { isSpace } from '../common/utils.mjs';
function StateBlock(src, md, env, tokens) {
var ch, s, start, pos, len, indent, offset, indent_found;
this.src = src;
// link to parser instance
@ -54,11 +52,10 @@ function StateBlock(src, md, env, tokens) {
// Create caches
// Generate markers.
s = this.src;
indent_found = false;
const s = this.src;
for (start = pos = indent = offset = 0, len = s.length; pos < len; pos++) {
ch = s.charCodeAt(pos);
for (let start = 0, pos = 0, indent = 0, offset = 0, len = s.length, indent_found = false; pos < len; pos++) {
const ch = s.charCodeAt(pos);
if (!indent_found) {
if (isSpace(ch)) {
@ -103,7 +100,7 @@ function StateBlock(src, md, env, tokens) {
// Push new token to "stream".
//
StateBlock.prototype.push = function (type, tag, nesting) {
var token = new Token(type, tag, nesting);
const token = new Token(type, tag, nesting);
token.block = true;
if (nesting < 0) this.level--; // closing tag
@ -119,7 +116,7 @@ StateBlock.prototype.isEmpty = function isEmpty(line) {
};
StateBlock.prototype.skipEmptyLines = function skipEmptyLines(from) {
for (var max = this.lineMax; from < max; from++) {
for (let max = this.lineMax; from < max; from++) {
if (this.bMarks[from] + this.tShift[from] < this.eMarks[from]) {
break;
}
@ -129,10 +126,8 @@ StateBlock.prototype.skipEmptyLines = function skipEmptyLines(from) {
// Skip spaces from given position.
StateBlock.prototype.skipSpaces = function skipSpaces(pos) {
var ch;
for (var max = this.src.length; pos < max; pos++) {
ch = this.src.charCodeAt(pos);
for (let max = this.src.length; pos < max; pos++) {
const ch = this.src.charCodeAt(pos);
if (!isSpace(ch)) { break; }
}
return pos;
@ -150,7 +145,7 @@ StateBlock.prototype.skipSpacesBack = function skipSpacesBack(pos, min) {
// Skip char codes from given position
StateBlock.prototype.skipChars = function skipChars(pos, code) {
for (var max = this.src.length; pos < max; pos++) {
for (let max = this.src.length; pos < max; pos++) {
if (this.src.charCodeAt(pos) !== code) { break; }
}
return pos;
@ -168,18 +163,17 @@ StateBlock.prototype.skipCharsBack = function skipCharsBack(pos, code, min) {
// cut lines range from source.
StateBlock.prototype.getLines = function getLines(begin, end, indent, keepLastLF) {
var i, lineIndent, ch, first, last, queue, lineStart,
line = begin;
if (begin >= end) {
return '';
}
queue = new Array(end - begin);
const queue = new Array(end - begin);
for (i = 0; line < end; line++, i++) {
lineIndent = 0;
lineStart = first = this.bMarks[line];
for (let i = 0, line = begin; line < end; line++, i++) {
let lineIndent = 0;
const lineStart = this.bMarks[line];
let first = lineStart;
let last;
if (line + 1 < end || keepLastLF) {
// No need for bounds check because we have fake entry on tail.
@ -189,7 +183,7 @@ StateBlock.prototype.getLines = function getLines(begin, end, indent, keepLastLF
}
while (first < last && lineIndent < indent) {
ch = this.src.charCodeAt(first);
const ch = this.src.charCodeAt(first);
if (isSpace(ch)) {
if (ch === 0x09) {

115
lib/rules_block/table.mjs

@ -3,22 +3,21 @@
import { isSpace } from '../common/utils.mjs';
function getLine(state, line) {
var pos = state.bMarks[line] + state.tShift[line],
max = state.eMarks[line];
const pos = state.bMarks[line] + state.tShift[line],
max = state.eMarks[line];
return state.src.slice(pos, max);
}
function escapedSplit(str) {
var result = [],
pos = 0,
max = str.length,
ch,
isEscaped = false,
lastPos = 0,
current = '';
const result = [];
const max = str.length;
ch = str.charCodeAt(pos);
let pos = 0;
let ch = str.charCodeAt(pos);
let isEscaped = false;
let lastPos = 0;
let current = '';
while (pos < max) {
if (ch === 0x7c/* | */) {
@ -47,14 +46,10 @@ function escapedSplit(str) {
export default function table(state, startLine, endLine, silent) {
var ch, lineText, pos, i, l, nextLine, columns, columnCount, token,
aligns, t, tableLines, tbodyLines, oldParentType, terminate,
terminatorRules, firstCh, secondCh;
// should have at least two lines
if (startLine + 2 > endLine) { return false; }
nextLine = startLine + 1;
let nextLine = startLine + 1;
if (state.sCount[nextLine] < state.blkIndent) { return false; }
@ -65,15 +60,15 @@ export default function table(state, startLine, endLine, silent) {
// and no other characters are allowed but spaces;
// basically, this is the equivalent of /^[-:|][-:|\s]*$/ regexp
pos = state.bMarks[nextLine] + state.tShift[nextLine];
let pos = state.bMarks[nextLine] + state.tShift[nextLine];
if (pos >= state.eMarks[nextLine]) { return false; }
firstCh = state.src.charCodeAt(pos++);
const firstCh = state.src.charCodeAt(pos++);
if (firstCh !== 0x7C/* | */ && firstCh !== 0x2D/* - */ && firstCh !== 0x3A/* : */) { return false; }
if (pos >= state.eMarks[nextLine]) { return false; }
secondCh = state.src.charCodeAt(pos++);
const secondCh = state.src.charCodeAt(pos++);
if (secondCh !== 0x7C/* | */ && secondCh !== 0x2D/* - */ && secondCh !== 0x3A/* : */ && !isSpace(secondCh)) {
return false;
}
@ -83,19 +78,18 @@ export default function table(state, startLine, endLine, silent) {
if (firstCh === 0x2D/* - */ && isSpace(secondCh)) { return false; }
while (pos < state.eMarks[nextLine]) {
ch = state.src.charCodeAt(pos);
const ch = state.src.charCodeAt(pos);
if (ch !== 0x7C/* | */ && ch !== 0x2D/* - */ && ch !== 0x3A/* : */ && !isSpace(ch)) { return false; }
pos++;
}
lineText = getLine(state, startLine + 1);
columns = lineText.split('|');
aligns = [];
for (i = 0; i < columns.length; i++) {
t = columns[i].trim();
let lineText = getLine(state, startLine + 1);
let columns = lineText.split('|');
const aligns = [];
for (let i = 0; i < columns.length; i++) {
const t = columns[i].trim();
if (!t) {
// allow empty columns before and after table, but not in between columns;
// e.g. allow ` |---| `, disallow ` ---||--- `
@ -125,48 +119,51 @@ export default function table(state, startLine, endLine, silent) {
// header row will define an amount of columns in the entire table,
// and align row should be exactly the same (the rest of the rows can differ)
columnCount = columns.length;
const columnCount = columns.length;
if (columnCount === 0 || columnCount !== aligns.length) { return false; }
if (silent) { return true; }
oldParentType = state.parentType;
const oldParentType = state.parentType;
state.parentType = 'table';
// use 'blockquote' lists for termination because it's
// the most similar to tables
terminatorRules = state.md.block.ruler.getRules('blockquote');
const terminatorRules = state.md.block.ruler.getRules('blockquote');
token = state.push('table_open', 'table', 1);
token.map = tableLines = [ startLine, 0 ];
const token_to = state.push('table_open', 'table', 1);
const tableLines = [ startLine, 0 ];
token_to.map = tableLines;
token = state.push('thead_open', 'thead', 1);
token.map = [ startLine, startLine + 1 ];
const token_tho = state.push('thead_open', 'thead', 1);
token_tho.map = [ startLine, startLine + 1 ];
token = state.push('tr_open', 'tr', 1);
token.map = [ startLine, startLine + 1 ];
const token_htro = state.push('tr_open', 'tr', 1);
token_htro.map = [ startLine, startLine + 1 ];
for (i = 0; i < columns.length; i++) {
token = state.push('th_open', 'th', 1);
for (let i = 0; i < columns.length; i++) {
const token_ho = state.push('th_open', 'th', 1);
if (aligns[i]) {
token.attrs = [ [ 'style', 'text-align:' + aligns[i] ] ];
token_ho.attrs = [ [ 'style', 'text-align:' + aligns[i] ] ];
}
token = state.push('inline', '', 0);
token.content = columns[i].trim();
token.children = [];
const token_il = state.push('inline', '', 0);
token_il.content = columns[i].trim();
token_il.children = [];
token = state.push('th_close', 'th', -1);
state.push('th_close', 'th', -1);
}
token = state.push('tr_close', 'tr', -1);
token = state.push('thead_close', 'thead', -1);
state.push('tr_close', 'tr', -1);
state.push('thead_close', 'thead', -1);
let tbodyLines;
for (nextLine = startLine + 2; nextLine < endLine; nextLine++) {
if (state.sCount[nextLine] < state.blkIndent) { break; }
terminate = false;
for (i = 0, l = terminatorRules.length; i < l; i++) {
let terminate = false;
for (let i = 0, l = terminatorRules.length; i < l; i++) {
if (terminatorRules[i](state, nextLine, endLine, true)) {
terminate = true;
break;
@ -182,34 +179,34 @@ export default function table(state, startLine, endLine, silent) {
if (columns.length && columns[columns.length - 1] === '') columns.pop();
if (nextLine === startLine + 2) {
token = state.push('tbody_open', 'tbody', 1);
token.map = tbodyLines = [ startLine + 2, 0 ];
const token_tbo = state.push('tbody_open', 'tbody', 1);
token_tbo.map = tbodyLines = [ startLine + 2, 0 ];
}
token = state.push('tr_open', 'tr', 1);
token.map = [ nextLine, nextLine + 1 ];
const token_tro = state.push('tr_open', 'tr', 1);
token_tro.map = [ nextLine, nextLine + 1 ];
for (i = 0; i < columnCount; i++) {
token = state.push('td_open', 'td', 1);
for (let i = 0; i < columnCount; i++) {
const token_tdo = state.push('td_open', 'td', 1);
if (aligns[i]) {
token.attrs = [ [ 'style', 'text-align:' + aligns[i] ] ];
token_tdo.attrs = [ [ 'style', 'text-align:' + aligns[i] ] ];
}
token = state.push('inline', '', 0);
token.content = columns[i] ? columns[i].trim() : '';
token.children = [];
const token_il = state.push('inline', '', 0);
token_il.content = columns[i] ? columns[i].trim() : '';
token_il.children = [];
token = state.push('td_close', 'td', -1);
state.push('td_close', 'td', -1);
}
token = state.push('tr_close', 'tr', -1);
state.push('tr_close', 'tr', -1);
}
if (tbodyLines) {
token = state.push('tbody_close', 'tbody', -1);
state.push('tbody_close', 'tbody', -1);
tbodyLines[1] = nextLine;
}
token = state.push('table_close', 'table', -1);
state.push('table_close', 'table', -1);
tableLines[1] = nextLine;
state.parentType = oldParentType;

2
lib/rules_core/block.mjs

@ -1,5 +1,5 @@
export default function block(state) {
var token;
let token;
if (state.inlineMode) {
token = new state.Token('inline', '', 0);

6
lib/rules_core/inline.mjs

@ -1,9 +1,9 @@
export default function inline(state) {
var tokens = state.tokens, tok, i, l;
const tokens = state.tokens;
// Parse inlines
for (i = 0, l = tokens.length; i < l; i++) {
tok = tokens[i];
for (let i = 0, l = tokens.length; i < l; i++) {
const tok = tokens[i];
if (tok.type === 'inline') {
state.md.inline.parse(tok.content, state.md, state.env, tok.children);
}

69
lib/rules_core/linkify.mjs

@ -15,27 +15,24 @@ function isLinkClose(str) {
export default function linkify(state) {
var i, j, l, tokens, token, currentToken, nodes, ln, text, pos, lastPos,
level, htmlLinkLevel, url, fullUrl, urlText,
blockTokens = state.tokens,
links;
const blockTokens = state.tokens;
if (!state.md.options.linkify) { return; }
for (j = 0, l = blockTokens.length; j < l; j++) {
for (let j = 0, l = blockTokens.length; j < l; j++) {
if (blockTokens[j].type !== 'inline' ||
!state.md.linkify.pretest(blockTokens[j].content)) {
continue;
}
tokens = blockTokens[j].children;
let tokens = blockTokens[j].children;
htmlLinkLevel = 0;
let htmlLinkLevel = 0;
// We scan from the end, to keep position when new tags added.
// Use reversed logic in links start/end match
for (i = tokens.length - 1; i >= 0; i--) {
currentToken = tokens[i];
for (let i = tokens.length - 1; i >= 0; i--) {
const currentToken = tokens[i];
// Skip content of markdown links
if (currentToken.type === 'link_close') {
@ -59,13 +56,13 @@ export default function linkify(state) {
if (currentToken.type === 'text' && state.md.linkify.test(currentToken.content)) {
text = currentToken.content;
links = state.md.linkify.match(text);
const text = currentToken.content;
let links = state.md.linkify.match(text);
// Now split string to nodes
nodes = [];
level = currentToken.level;
lastPos = 0;
const nodes = [];
let level = currentToken.level;
let lastPos = 0;
// forbid escape sequence at the start of the string,
// this avoids http\://example.com/ from being linkified as
@ -77,12 +74,12 @@ export default function linkify(state) {
links = links.slice(1);
}
for (ln = 0; ln < links.length; ln++) {
url = links[ln].url;
fullUrl = state.md.normalizeLink(url);
for (let ln = 0; ln < links.length; ln++) {
const url = links[ln].url;
const fullUrl = state.md.normalizeLink(url);
if (!state.md.validateLink(fullUrl)) { continue; }
urlText = links[ln].text;
let urlText = links[ln].text;
// Linkifier might send raw hostnames like "example.com", where url
// starts with domain name. So we prepend http:// in those cases,
@ -96,37 +93,37 @@ export default function linkify(state) {
urlText = state.md.normalizeLinkText(urlText);
}
pos = links[ln].index;
const pos = links[ln].index;
if (pos > lastPos) {
token = new state.Token('text', '', 0);
const token = new state.Token('text', '', 0);
token.content = text.slice(lastPos, pos);
token.level = level;
nodes.push(token);
}
token = new state.Token('link_open', 'a', 1);
token.attrs = [ [ 'href', fullUrl ] ];
token.level = level++;
token.markup = 'linkify';
token.info = 'auto';
nodes.push(token);
const token_o = new state.Token('link_open', 'a', 1);
token_o.attrs = [ [ 'href', fullUrl ] ];
token_o.level = level++;
token_o.markup = 'linkify';
token_o.info = 'auto';
nodes.push(token_o);
token = new state.Token('text', '', 0);
token.content = urlText;
token.level = level;
nodes.push(token);
const token_t = new state.Token('text', '', 0);
token_t.content = urlText;
token_t.level = level;
nodes.push(token_t);
token = new state.Token('link_close', 'a', -1);
token.level = --level;
token.markup = 'linkify';
token.info = 'auto';
nodes.push(token);
const token_c = new state.Token('link_close', 'a', -1);
token_c.level = --level;
token_c.markup = 'linkify';
token_c.info = 'auto';
nodes.push(token_c);
lastPos = links[ln].lastIndex;
}
if (lastPos < text.length) {
token = new state.Token('text', '', 0);
const token = new state.Token('text', '', 0);
token.content = text.slice(lastPos);
token.level = level;
nodes.push(token);

6
lib/rules_core/normalize.mjs

@ -2,12 +2,12 @@
// https://spec.commonmark.org/0.29/#line-ending
var NEWLINES_RE = /\r\n?|\n/g;
var NULL_RE = /\0/g;
const NEWLINES_RE = /\r\n?|\n/g;
const NULL_RE = /\0/g;
export default function normalize(state) {
var str;
let str;
// Normalize newlines
str = state.src.replace(NEWLINES_RE, '\n');

14
lib/rules_core/replacements.mjs

@ -13,14 +13,14 @@
// - fractionals 1/2, 1/4, 3/4 -> ½, ¼, ¾
// - multiplications 2 x 4 -> 2 × 4
var RARE_RE = /\+-|\.\.|\?\?\?\?|!!!!|,,|--/;
const RARE_RE = /\+-|\.\.|\?\?\?\?|!!!!|,,|--/;
// Workaround for phantomjs - need regex without /g flag,
// or root check will fail every second time
var SCOPED_ABBR_TEST_RE = /\((c|tm|r)\)/i;
const SCOPED_ABBR_TEST_RE = /\((c|tm|r)\)/i;
var SCOPED_ABBR_RE = /\((c|tm|r)\)/ig;
var SCOPED_ABBR = {
const SCOPED_ABBR_RE = /\((c|tm|r)\)/ig;
const SCOPED_ABBR = {
c: '©',
r: '®',
tm: '™'
@ -31,7 +31,7 @@ function replaceFn(match, name) {
}
function replace_scoped(inlineTokens) {
var i, token, inside_autolink = 0;
let i, token, inside_autolink = 0;
for (i = inlineTokens.length - 1; i >= 0; i--) {
token = inlineTokens[i];
@ -51,7 +51,7 @@ function replace_scoped(inlineTokens) {
}
function replace_rare(inlineTokens) {
var i, token, inside_autolink = 0;
let i, token, inside_autolink = 0;
for (i = inlineTokens.length - 1; i >= 0; i--) {
token = inlineTokens[i];
@ -84,7 +84,7 @@ function replace_rare(inlineTokens) {
export default function replace(state) {
var blkIdx;
let blkIdx;
if (!state.md.options.typographer) { return; }

51
lib/rules_core/smartquotes.mjs

@ -3,9 +3,9 @@
import { isWhiteSpace, isPunctChar, isMdAsciiPunct } from '../common/utils.mjs';
var QUOTE_TEST_RE = /['"]/;
var QUOTE_RE = /['"]/g;
var APOSTROPHE = '\u2019'; /* ’ */
const QUOTE_TEST_RE = /['"]/;
const QUOTE_RE = /['"]/g;
const APOSTROPHE = '\u2019'; /* ’ */
function replaceAt(str, index, ch) {
@ -13,16 +13,14 @@ function replaceAt(str, index, ch) {
}
function process_inlines(tokens, state) {
var i, token, text, t, pos, max, thisLevel, item, lastChar, nextChar,
isLastPunctChar, isNextPunctChar, isLastWhiteSpace, isNextWhiteSpace,
canOpen, canClose, j, isSingle, stack, openQuote, closeQuote;
let j;
stack = [];
const stack = [];
for (i = 0; i < tokens.length; i++) {
token = tokens[i];
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
thisLevel = tokens[i].level;
const thisLevel = tokens[i].level;
for (j = stack.length - 1; j >= 0; j--) {
if (stack[j].level <= thisLevel) { break; }
@ -31,25 +29,26 @@ function process_inlines(tokens, state) {
if (token.type !== 'text') { continue; }
text = token.content;
pos = 0;
max = text.length;
let text = token.content;
let pos = 0;
let max = text.length;
/*eslint no-labels:0,block-scoped-var:0*/
OUTER:
while (pos < max) {
QUOTE_RE.lastIndex = pos;
t = QUOTE_RE.exec(text);
const t = QUOTE_RE.exec(text);
if (!t) { break; }
canOpen = canClose = true;
let canOpen = true;
let canClose = true;
pos = t.index + 1;
isSingle = (t[0] === "'");
const isSingle = (t[0] === "'");
// Find previous character,
// default to space if it's the beginning of the line
//
lastChar = 0x20;
let lastChar = 0x20;
if (t.index - 1 >= 0) {
lastChar = text.charCodeAt(t.index - 1);
@ -66,7 +65,7 @@ function process_inlines(tokens, state) {
// Find next character,
// default to space if it's the end of the line
//
nextChar = 0x20;
let nextChar = 0x20;
if (pos < max) {
nextChar = text.charCodeAt(pos);
@ -80,11 +79,11 @@ function process_inlines(tokens, state) {
}
}
isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(String.fromCharCode(lastChar));
isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(String.fromCharCode(nextChar));
const isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(String.fromCharCode(lastChar));
const isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(String.fromCharCode(nextChar));
isLastWhiteSpace = isWhiteSpace(lastChar);
isNextWhiteSpace = isWhiteSpace(nextChar);
const isLastWhiteSpace = isWhiteSpace(lastChar);
const isNextWhiteSpace = isWhiteSpace(nextChar);
if (isNextWhiteSpace) {
canOpen = false;
@ -132,11 +131,13 @@ function process_inlines(tokens, state) {
if (canClose) {
// this could be a closing quote, rewind the stack to get a match
for (j = stack.length - 1; j >= 0; j--) {
item = stack[j];
let item = stack[j];
if (stack[j].level < thisLevel) { break; }
if (item.single === isSingle && stack[j].level === thisLevel) {
item = stack[j];
let openQuote;
let closeQuote;
if (isSingle) {
openQuote = state.md.options.quotes[2];
closeQuote = state.md.options.quotes[3];
@ -181,11 +182,9 @@ function process_inlines(tokens, state) {
export default function smartquotes(state) {
/*eslint max-depth:0*/
var blkIdx;
if (!state.md.options.typographer) { return; }
for (blkIdx = state.tokens.length - 1; blkIdx >= 0; blkIdx--) {
for (let blkIdx = state.tokens.length - 1; blkIdx >= 0; blkIdx--) {
if (state.tokens[blkIdx].type !== 'inline' ||
!QUOTE_TEST_RE.test(state.tokens[blkIdx].content)) {

11
lib/rules_core/text_join.mjs

@ -7,14 +7,15 @@
//
export default function text_join(state) {
var j, l, tokens, curr, max, last,
blockTokens = state.tokens;
let curr, last;
const blockTokens = state.tokens;
const l = blockTokens.length;
for (j = 0, l = blockTokens.length; j < l; j++) {
for (let j = 0; j < l; j++) {
if (blockTokens[j].type !== 'inline') continue;
tokens = blockTokens[j].children;
max = tokens.length;
const tokens = blockTokens[j].children;
const max = tokens.length;
for (curr = 0; curr < max; curr++) {
if (tokens[curr].type === 'text_special') {

55
lib/rules_inline/autolink.mjs

@ -1,46 +1,45 @@
// Process autolinks '<protocol:...>'
/*eslint max-len:0*/
var EMAIL_RE = /^([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)$/;
var AUTOLINK_RE = /^([a-zA-Z][a-zA-Z0-9+.\-]{1,31}):([^<>\x00-\x20]*)$/;
const EMAIL_RE = /^([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)$/;
const AUTOLINK_RE = /^([a-zA-Z][a-zA-Z0-9+.\-]{1,31}):([^<>\x00-\x20]*)$/;
export default function autolink(state, silent) {
var url, fullUrl, token, ch, start, max,
pos = state.pos;
let pos = state.pos;
if (state.src.charCodeAt(pos) !== 0x3C/* < */) { return false; }
start = state.pos;
max = state.posMax;
const start = state.pos;
const max = state.posMax;
for (;;) {
if (++pos >= max) return false;
ch = state.src.charCodeAt(pos);
const ch = state.src.charCodeAt(pos);
if (ch === 0x3C /* < */) return false;
if (ch === 0x3E /* > */) break;
}
url = state.src.slice(start + 1, pos);
const url = state.src.slice(start + 1, pos);
if (AUTOLINK_RE.test(url)) {
fullUrl = state.md.normalizeLink(url);
const fullUrl = state.md.normalizeLink(url);
if (!state.md.validateLink(fullUrl)) { return false; }
if (!silent) {
token = state.push('link_open', 'a', 1);
token.attrs = [ [ 'href', fullUrl ] ];
token.markup = 'autolink';
token.info = 'auto';
const token_o = state.push('link_open', 'a', 1);
token_o.attrs = [ [ 'href', fullUrl ] ];
token_o.markup = 'autolink';
token_o.info = 'auto';
token = state.push('text', '', 0);
token.content = state.md.normalizeLinkText(url);
const token_t = state.push('text', '', 0);
token_t.content = state.md.normalizeLinkText(url);
token = state.push('link_close', 'a', -1);
token.markup = 'autolink';
token.info = 'auto';
const token_c = state.push('link_close', 'a', -1);
token_c.markup = 'autolink';
token_c.info = 'auto';
}
state.pos += url.length + 2;
@ -48,21 +47,21 @@ export default function autolink(state, silent) {
}
if (EMAIL_RE.test(url)) {
fullUrl = state.md.normalizeLink('mailto:' + url);
const fullUrl = state.md.normalizeLink('mailto:' + url);
if (!state.md.validateLink(fullUrl)) { return false; }
if (!silent) {
token = state.push('link_open', 'a', 1);
token.attrs = [ [ 'href', fullUrl ] ];
token.markup = 'autolink';
token.info = 'auto';
const token_o = state.push('link_open', 'a', 1);
token_o.attrs = [ [ 'href', fullUrl ] ];
token_o.markup = 'autolink';
token_o.info = 'auto';
token = state.push('text', '', 0);
token.content = state.md.normalizeLinkText(url);
const token_t = state.push('text', '', 0);
token_t.content = state.md.normalizeLinkText(url);
token = state.push('link_close', 'a', -1);
token.markup = 'autolink';
token.info = 'auto';
const token_c = state.push('link_close', 'a', -1);
token_c.markup = 'autolink';
token_c.info = 'auto';
}
state.pos += url.length + 2;

22
lib/rules_inline/backticks.mjs

@ -1,21 +1,20 @@
// Parse backticks
export default function backtick(state, silent) {
var start, max, marker, token, matchStart, matchEnd, openerLength, closerLength,
pos = state.pos,
ch = state.src.charCodeAt(pos);
let pos = state.pos;
const ch = state.src.charCodeAt(pos);
if (ch !== 0x60/* ` */) { return false; }
start = pos;
const start = pos;
pos++;
max = state.posMax;
const max = state.posMax;
// scan marker length
while (pos < max && state.src.charCodeAt(pos) === 0x60/* ` */) { pos++; }
marker = state.src.slice(start, pos);
openerLength = marker.length;
const marker = state.src.slice(start, pos);
const openerLength = marker.length;
if (state.backticksScanned && (state.backticks[openerLength] || 0) <= start) {
if (!silent) state.pending += marker;
@ -23,7 +22,8 @@ export default function backtick(state, silent) {
return true;
}
matchEnd = pos;
let matchEnd = pos;
let matchStart;
// Nothing found in the cache, scan until the end of the line (or until marker is found)
while ((matchStart = state.src.indexOf('`', matchEnd)) !== -1) {
@ -32,13 +32,13 @@ export default function backtick(state, silent) {
// scan marker length
while (matchEnd < max && state.src.charCodeAt(matchEnd) === 0x60/* ` */) { matchEnd++; }
closerLength = matchEnd - matchStart;
const closerLength = matchEnd - matchStart;
if (closerLength === openerLength) {
// Found matching closer length.
if (!silent) {
token = state.push('code_inline', 'code', 0);
token.markup = marker;
const token = state.push('code_inline', 'code', 0);
token.markup = marker;
token.content = state.src.slice(pos, matchStart)
.replace(/\n/g, ' ')
.replace(/^ (.+) $/, '$1');

35
lib/rules_inline/balance_pairs.mjs

@ -2,20 +2,18 @@
//
function processDelimiters(delimiters) {
var closerIdx, openerIdx, closer, opener, minOpenerIdx, newMinOpenerIdx,
isOddMatch, lastJump,
openersBottom = {},
max = delimiters.length;
const openersBottom = {};
const max = delimiters.length;
if (!max) return;
// headerIdx is the first delimiter of the current (where closer is) delimiter run
var headerIdx = 0;
var lastTokenIdx = -2; // needs any value lower than -1
var jumps = [];
let headerIdx = 0;
let lastTokenIdx = -2; // needs any value lower than -1
const jumps = [];
for (closerIdx = 0; closerIdx < max; closerIdx++) {
closer = delimiters[closerIdx];
for (let closerIdx = 0; closerIdx < max; closerIdx++) {
const closer = delimiters[closerIdx];
jumps.push(0);
@ -45,20 +43,20 @@ function processDelimiters(delimiters) {
openersBottom[closer.marker] = [ -1, -1, -1, -1, -1, -1 ];
}
minOpenerIdx = openersBottom[closer.marker][(closer.open ? 3 : 0) + (closer.length % 3)];
const minOpenerIdx = openersBottom[closer.marker][(closer.open ? 3 : 0) + (closer.length % 3)];
openerIdx = headerIdx - jumps[headerIdx] - 1;
let openerIdx = headerIdx - jumps[headerIdx] - 1;
newMinOpenerIdx = openerIdx;
let newMinOpenerIdx = openerIdx;
for (; openerIdx > minOpenerIdx; openerIdx -= jumps[openerIdx] + 1) {
opener = delimiters[openerIdx];
const opener = delimiters[openerIdx];
if (opener.marker !== closer.marker) continue;
if (opener.open && opener.end < 0) {
isOddMatch = false;
let isOddMatch = false;
// from spec:
//
@ -80,7 +78,7 @@ function processDelimiters(delimiters) {
// the entire sequence in future checks. This is required to make
// sure algorithm has linear complexity (see *_*_*_*_*_... case).
//
lastJump = openerIdx > 0 && !delimiters[openerIdx - 1].open ?
const lastJump = openerIdx > 0 && !delimiters[openerIdx - 1].open ?
jumps[openerIdx - 1] + 1 :
0;
@ -114,13 +112,12 @@ function processDelimiters(delimiters) {
export default function link_pairs(state) {
var curr,
tokens_meta = state.tokens_meta,
max = state.tokens_meta.length;
const tokens_meta = state.tokens_meta;
const max = state.tokens_meta.length;
processDelimiters(state.delimiters);
for (curr = 0; curr < max; curr++) {
for (let curr = 0; curr < max; curr++) {
if (tokens_meta[curr] && tokens_meta[curr].delimiters) {
processDelimiters(tokens_meta[curr].delimiters);
}

60
lib/rules_inline/emphasis.mjs

@ -4,18 +4,17 @@
// Insert each marker as a separate text token, and add it to delimiter list
//
function emphasis_tokenize(state, silent) {
var i, scanned, token,
start = state.pos,
marker = state.src.charCodeAt(start);
const start = state.pos;
const marker = state.src.charCodeAt(start);
if (silent) { return false; }
if (marker !== 0x5F /* _ */ && marker !== 0x2A /* * */) { return false; }
scanned = state.scanDelims(state.pos, marker === 0x2A);
const scanned = state.scanDelims(state.pos, marker === 0x2A);
for (i = 0; i < scanned.length; i++) {
token = state.push('text', '', 0);
for (let i = 0; i < scanned.length; i++) {
const token = state.push('text', '', 0);
token.content = String.fromCharCode(marker);
state.delimiters.push({
@ -51,16 +50,10 @@ function emphasis_tokenize(state, silent) {
function postProcess(state, delimiters) {
var i,
startDelim,
endDelim,
token,
ch,
isStrong,
max = delimiters.length;
const max = delimiters.length;
for (i = max - 1; i >= 0; i--) {
startDelim = delimiters[i];
for (let i = max - 1; i >= 0; i--) {
const startDelim = delimiters[i];
if (startDelim.marker !== 0x5F/* _ */ && startDelim.marker !== 0x2A/* * */) {
continue;
@ -71,14 +64,14 @@ function postProcess(state, delimiters) {
continue;
}
endDelim = delimiters[startDelim.end];
const endDelim = delimiters[startDelim.end];
// If the previous delimiter has the same marker and is adjacent to this one,
// merge those into one strong delimiter.
//
// `<em><em>whatever</em></em>` -> `<strong>whatever</strong>`
//
isStrong = i > 0 &&
const isStrong = i > 0 &&
delimiters[i - 1].end === startDelim.end + 1 &&
// check that first two markers match and adjacent
delimiters[i - 1].marker === startDelim.marker &&
@ -86,21 +79,21 @@ function postProcess(state, delimiters) {
// check that last two markers are adjacent (we can safely assume they match)
delimiters[startDelim.end + 1].token === endDelim.token + 1;
ch = String.fromCharCode(startDelim.marker);
const ch = String.fromCharCode(startDelim.marker);
token = state.tokens[startDelim.token];
token.type = isStrong ? 'strong_open' : 'em_open';
token.tag = isStrong ? 'strong' : 'em';
token.nesting = 1;
token.markup = isStrong ? ch + ch : ch;
token.content = '';
const token_o = state.tokens[startDelim.token];
token_o.type = isStrong ? 'strong_open' : 'em_open';
token_o.tag = isStrong ? 'strong' : 'em';
token_o.nesting = 1;
token_o.markup = isStrong ? ch + ch : ch;
token_o.content = '';
token = state.tokens[endDelim.token];
token.type = isStrong ? 'strong_close' : 'em_close';
token.tag = isStrong ? 'strong' : 'em';
token.nesting = -1;
token.markup = isStrong ? ch + ch : ch;
token.content = '';
const token_c = state.tokens[endDelim.token];
token_c.type = isStrong ? 'strong_close' : 'em_close';
token_c.tag = isStrong ? 'strong' : 'em';
token_c.nesting = -1;
token_c.markup = isStrong ? ch + ch : ch;
token_c.content = '';
if (isStrong) {
state.tokens[delimiters[i - 1].token].content = '';
@ -114,13 +107,12 @@ function postProcess(state, delimiters) {
// Walk through delimiter list and replace text tokens with tags
//
function emphasis_post_process(state) {
var curr,
tokens_meta = state.tokens_meta,
max = state.tokens_meta.length;
const tokens_meta = state.tokens_meta;
const max = state.tokens_meta.length;
postProcess(state, state.delimiters);
for (curr = 0; curr < max; curr++) {
for (let curr = 0; curr < max; curr++) {
if (tokens_meta[curr] && tokens_meta[curr].delimiters) {
postProcess(state, tokens_meta[curr].delimiters);
}

21
lib/rules_inline/entity.mjs

@ -4,26 +4,27 @@ import { decodeHTML } from 'entities';
import { isValidEntityCode, fromCodePoint } from '../common/utils.mjs';
var DIGITAL_RE = /^&#((?:x[a-f0-9]{1,6}|[0-9]{1,7}));/i;
var NAMED_RE = /^&([a-z][a-z0-9]{1,31});/i;
const DIGITAL_RE = /^&#((?:x[a-f0-9]{1,6}|[0-9]{1,7}));/i;
const NAMED_RE = /^&([a-z][a-z0-9]{1,31});/i;
export default function entity(state, silent) {
var ch, code, match, decoded, token, pos = state.pos, max = state.posMax;
const pos = state.pos;
const max = state.posMax;
if (state.src.charCodeAt(pos) !== 0x26/* & */) return false;
if (pos + 1 >= max) return false;
ch = state.src.charCodeAt(pos + 1);
const ch = state.src.charCodeAt(pos + 1);
if (ch === 0x23 /* # */) {
match = state.src.slice(pos).match(DIGITAL_RE);
const match = state.src.slice(pos).match(DIGITAL_RE);
if (match) {
if (!silent) {
code = match[1][0].toLowerCase() === 'x' ? parseInt(match[1].slice(1), 16) : parseInt(match[1], 10);
const code = match[1][0].toLowerCase() === 'x' ? parseInt(match[1].slice(1), 16) : parseInt(match[1], 10);
token = state.push('text_special', '', 0);
const token = state.push('text_special', '', 0);
token.content = isValidEntityCode(code) ? fromCodePoint(code) : fromCodePoint(0xFFFD);
token.markup = match[0];
token.info = 'entity';
@ -32,12 +33,12 @@ export default function entity(state, silent) {
return true;
}
} else {
match = state.src.slice(pos).match(NAMED_RE);
const match = state.src.slice(pos).match(NAMED_RE);
if (match) {
decoded = decodeHTML(match[0]);
const decoded = decodeHTML(match[0]);
if (decoded !== match[0]) {
if (!silent) {
token = state.push('text_special', '', 0);
const token = state.push('text_special', '', 0);
token.content = decoded;
token.markup = match[0];
token.info = 'entity';

17
lib/rules_inline/escape.mjs

@ -2,16 +2,17 @@
import { isSpace } from '../common/utils.mjs';
var ESCAPED = [];
const ESCAPED = [];
for (var i = 0; i < 256; i++) { ESCAPED.push(0); }
for (let i = 0; i < 256; i++) { ESCAPED.push(0); }
'\\!"#$%&\'()*+,./:;<=>?@[]^_`{|}~-'
.split('').forEach(function (ch) { ESCAPED[ch.charCodeAt(0)] = 1; });
export default function escape(state, silent) {
var ch1, ch2, origStr, escapedStr, token, pos = state.pos, max = state.posMax;
let pos = state.pos;
const max = state.posMax;
if (state.src.charCodeAt(pos) !== 0x5C/* \ */) return false;
pos++;
@ -19,7 +20,7 @@ export default function escape(state, silent) {
// '\' at the end of the inline block
if (pos >= max) return false;
ch1 = state.src.charCodeAt(pos);
let ch1 = state.src.charCodeAt(pos);
if (ch1 === 0x0A) {
if (!silent) {
@ -38,10 +39,10 @@ export default function escape(state, silent) {
return true;
}
escapedStr = state.src[pos];
let escapedStr = state.src[pos];
if (ch1 >= 0xD800 && ch1 <= 0xDBFF && pos + 1 < max) {
ch2 = state.src.charCodeAt(pos + 1);
const ch2 = state.src.charCodeAt(pos + 1);
if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) {
escapedStr += state.src[pos + 1];
@ -49,10 +50,10 @@ export default function escape(state, silent) {
}
}
origStr = '\\' + escapedStr;
const origStr = '\\' + escapedStr;
if (!silent) {
token = state.push('text_special', '', 0);
const token = state.push('text_special', '', 0);
if (ch1 < 256 && ESCAPED[ch1] !== 0) {
token.content = escapedStr;

8
lib/rules_inline/fragments_join.mjs

@ -8,10 +8,10 @@
//
export default function fragments_join(state) {
var curr, last,
level = 0,
tokens = state.tokens,
max = state.tokens.length;
let curr, last,
level = 0;
const tokens = state.tokens;
const max = state.tokens.length;
for (curr = last = 0; curr < max; curr++) {
// re-calculate levels after emphasis/strikethrough turns some text nodes

14
lib/rules_inline/html_inline.mjs

@ -13,26 +13,24 @@ function isLinkClose(str) {
function isLetter(ch) {
/*eslint no-bitwise:0*/
var lc = ch | 0x20; // to lower case
const lc = ch | 0x20; // to lower case
return (lc >= 0x61/* a */) && (lc <= 0x7a/* z */);
}
export default function html_inline(state, silent) {
var ch, match, max, token,
pos = state.pos;
if (!state.md.options.html) { return false; }
// Check start
max = state.posMax;
const max = state.posMax;
const pos = state.pos;
if (state.src.charCodeAt(pos) !== 0x3C/* < */ ||
pos + 2 >= max) {
return false;
}
// Quick fail on second char
ch = state.src.charCodeAt(pos + 1);
const ch = state.src.charCodeAt(pos + 1);
if (ch !== 0x21/* ! */ &&
ch !== 0x3F/* ? */ &&
ch !== 0x2F/* / */ &&
@ -40,11 +38,11 @@ export default function html_inline(state, silent) {
return false;
}
match = state.src.slice(pos).match(HTML_TAG_RE);
const match = state.src.slice(pos).match(HTML_TAG_RE);
if (!match) { return false; }
if (!silent) {
token = state.push('html_inline', '', 0);
const token = state.push('html_inline', '', 0);
token.content = match[0];
if (isLinkOpen(token.content)) state.linkLevel++;

27
lib/rules_inline/image.mjs

@ -4,28 +4,23 @@ import { normalizeReference, isSpace } from '../common/utils.mjs';
export default function image(state, silent) {
var attrs,
code,
let code,
content,
label,
labelEnd,
labelStart,
pos,
ref,
res,
title,
token,
tokens,
start,
href = '',
oldPos = state.pos,
max = state.posMax;
href = '';
const oldPos = state.pos;
const max = state.posMax;
if (state.src.charCodeAt(state.pos) !== 0x21/* ! */) { return false; }
if (state.src.charCodeAt(state.pos + 1) !== 0x5B/* [ */) { return false; }
labelStart = state.pos + 2;
labelEnd = state.md.helpers.parseLinkLabel(state, state.pos + 1, false);
const labelStart = state.pos + 2;
const labelEnd = state.md.helpers.parseLinkLabel(state, state.pos + 1, false);
// parser failed to find ']', so it's not a valid link
if (labelEnd < 0) { return false; }
@ -126,17 +121,19 @@ export default function image(state, silent) {
if (!silent) {
content = state.src.slice(labelStart, labelEnd);
const tokens = [];
state.md.inline.parse(
content,
state.md,
state.env,
tokens = []
tokens
);
token = state.push('image', 'img', 0);
token.attrs = attrs = [ [ 'src', href ], [ 'alt', '' ] ];
const token = state.push('image', 'img', 0);
const attrs = [ [ 'src', href ], [ 'alt', '' ] ];
token.attrs = attrs;
token.children = tokens;
token.content = content;
token.content = content;
if (title) {
attrs.push([ 'title', title ]);

24
lib/rules_inline/link.mjs

@ -3,31 +3,26 @@
import { normalizeReference, isSpace } from '../common/utils.mjs';
export default function link(state, silent) {
var attrs,
code,
let code,
label,
labelEnd,
labelStart,
pos,
res,
ref,
token,
href = '',
title = '',
oldPos = state.pos,
max = state.posMax,
start = state.pos,
parseReference = true;
if (state.src.charCodeAt(state.pos) !== 0x5B/* [ */) { return false; }
labelStart = state.pos + 1;
labelEnd = state.md.helpers.parseLinkLabel(state, state.pos, true);
const oldPos = state.pos;
const max = state.posMax;
const labelStart = state.pos + 1;
const labelEnd = state.md.helpers.parseLinkLabel(state, state.pos, true);
// parser failed to find ']', so it's not a valid link
if (labelEnd < 0) { return false; }
pos = labelEnd + 1;
let pos = labelEnd + 1;
if (pos < max && state.src.charCodeAt(pos) === 0x28/* ( */) {
//
// Inline link
@ -127,8 +122,9 @@ export default function link(state, silent) {
state.pos = labelStart;
state.posMax = labelEnd;
token = state.push('link_open', 'a', 1);
token.attrs = attrs = [ [ 'href', href ] ];
const token_o = state.push('link_open', 'a', 1);
const attrs = [ [ 'href', href ] ];
token_o.attrs = attrs;
if (title) {
attrs.push([ 'title', title ]);
}
@ -137,7 +133,7 @@ export default function link(state, silent) {
state.md.inline.tokenize(state);
state.linkLevel--;
token = state.push('link_close', 'a', -1);
state.push('link_close', 'a', -1);
}
state.pos = pos;

36
lib/rules_inline/linkify.mjs

@ -1,32 +1,30 @@
// Process links like https://example.org/
// RFC3986: scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
var SCHEME_RE = /(?:^|[^a-z0-9.+-])([a-z][a-z0-9.+-]*)$/i;
const SCHEME_RE = /(?:^|[^a-z0-9.+-])([a-z][a-z0-9.+-]*)$/i;
export default function linkify(state, silent) {
var pos, max, match, proto, link, url, fullUrl, token;
if (!state.md.options.linkify) return false;
if (state.linkLevel > 0) return false;
pos = state.pos;
max = state.posMax;
const pos = state.pos;
const max = state.posMax;
if (pos + 3 > max) return false;
if (state.src.charCodeAt(pos) !== 0x3A/* : */) return false;
if (state.src.charCodeAt(pos + 1) !== 0x2F/* / */) return false;
if (state.src.charCodeAt(pos + 2) !== 0x2F/* / */) return false;
match = state.pending.match(SCHEME_RE);
const match = state.pending.match(SCHEME_RE);
if (!match) return false;
proto = match[1];
const proto = match[1];
link = state.md.linkify.matchAtStart(state.src.slice(pos - proto.length));
const link = state.md.linkify.matchAtStart(state.src.slice(pos - proto.length));
if (!link) return false;
url = link.url;
let url = link.url;
// invalid link, but still detected by linkify somehow;
// need to check to prevent infinite loop below
@ -35,23 +33,23 @@ export default function linkify(state, silent) {
// disallow '*' at the end of the link (conflicts with emphasis)
url = url.replace(/\*+$/, '');
fullUrl = state.md.normalizeLink(url);
const fullUrl = state.md.normalizeLink(url);
if (!state.md.validateLink(fullUrl)) return false;
if (!silent) {
state.pending = state.pending.slice(0, -proto.length);
token = state.push('link_open', 'a', 1);
token.attrs = [ [ 'href', fullUrl ] ];
token.markup = 'linkify';
token.info = 'auto';
const token_o = state.push('link_open', 'a', 1);
token_o.attrs = [ [ 'href', fullUrl ] ];
token_o.markup = 'linkify';
token_o.info = 'auto';
token = state.push('text', '', 0);
token.content = state.md.normalizeLinkText(url);
const token_t = state.push('text', '', 0);
token_t.content = state.md.normalizeLinkText(url);
token = state.push('link_close', 'a', -1);
token.markup = 'linkify';
token.info = 'auto';
const token_c = state.push('link_close', 'a', -1);
token_c.markup = 'linkify';
token_c.info = 'auto';
}
state.pos += url.length - proto.length;

8
lib/rules_inline/newline.mjs

@ -3,12 +3,12 @@
import { isSpace } from '../common/utils.mjs';
export default function newline(state, silent) {
var pmax, max, ws, pos = state.pos;
let pos = state.pos;
if (state.src.charCodeAt(pos) !== 0x0A/* \n */) { return false; }
pmax = state.pending.length - 1;
max = state.posMax;
const pmax = state.pending.length - 1;
const max = state.posMax;
// ' \n' -> hardbreak
// Lookup in pending chars is bad practice! Don't copy to other rules!
@ -18,7 +18,7 @@ export default function newline(state, silent) {
if (pmax >= 0 && state.pending.charCodeAt(pmax) === 0x20) {
if (pmax >= 1 && state.pending.charCodeAt(pmax - 1) === 0x20) {
// Find whitespaces tail of pending chars.
ws = pmax - 1;
let ws = pmax - 1;
while (ws >= 1 && state.pending.charCodeAt(ws - 1) === 0x20) ws--;
state.pending = state.pending.slice(0, ws);

33
lib/rules_inline/state_inline.mjs

@ -39,7 +39,7 @@ function StateInline(src, md, env, outTokens) {
// Flush pending text
//
StateInline.prototype.pushPending = function () {
var token = new Token('text', '', 0);
const token = new Token('text', '', 0);
token.content = this.pending;
token.level = this.pendingLevel;
this.tokens.push(token);
@ -56,8 +56,8 @@ StateInline.prototype.push = function (type, tag, nesting) {
this.pushPending();
}
var token = new Token(type, tag, nesting);
var token_meta = null;
const token = new Token(type, tag, nesting);
let token_meta = null;
if (nesting < 0) {
// closing tag
@ -89,29 +89,28 @@ StateInline.prototype.push = function (type, tag, nesting) {
// - canSplitWord - determine if these markers can be found inside a word
//
StateInline.prototype.scanDelims = function (start, canSplitWord) {
var pos = start, lastChar, nextChar, count, can_open, can_close,
isLastWhiteSpace, isLastPunctChar,
isNextWhiteSpace, isNextPunctChar,
left_flanking = true,
right_flanking = true,
max = this.posMax,
marker = this.src.charCodeAt(start);
let can_open, can_close;
let left_flanking = true;
let right_flanking = true;
const max = this.posMax;
const marker = this.src.charCodeAt(start);
// treat beginning of the line as a whitespace
lastChar = start > 0 ? this.src.charCodeAt(start - 1) : 0x20;
const lastChar = start > 0 ? this.src.charCodeAt(start - 1) : 0x20;
let pos = start;
while (pos < max && this.src.charCodeAt(pos) === marker) { pos++; }
count = pos - start;
const count = pos - start;
// treat end of the line as a whitespace
nextChar = pos < max ? this.src.charCodeAt(pos) : 0x20;
const nextChar = pos < max ? this.src.charCodeAt(pos) : 0x20;
isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(String.fromCharCode(lastChar));
isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(String.fromCharCode(nextChar));
const isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(String.fromCharCode(lastChar));
const isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(String.fromCharCode(nextChar));
isLastWhiteSpace = isWhiteSpace(lastChar);
isNextWhiteSpace = isWhiteSpace(nextChar);
const isLastWhiteSpace = isWhiteSpace(lastChar);
const isNextWhiteSpace = isWhiteSpace(nextChar);
if (isNextWhiteSpace) {
left_flanking = false;

41
lib/rules_inline/strikethrough.mjs

@ -4,27 +4,28 @@
// Insert each marker as a separate text token, and add it to delimiter list
//
function strikethrough_tokenize(state, silent) {
var i, scanned, token, len, ch,
start = state.pos,
marker = state.src.charCodeAt(start);
const start = state.pos;
const marker = state.src.charCodeAt(start);
if (silent) { return false; }
if (marker !== 0x7E/* ~ */) { return false; }
scanned = state.scanDelims(state.pos, true);
len = scanned.length;
ch = String.fromCharCode(marker);
const scanned = state.scanDelims(state.pos, true);
let len = scanned.length;
const ch = String.fromCharCode(marker);
if (len < 2) { return false; }
let token;
if (len % 2) {
token = state.push('text', '', 0);
token.content = ch;
len--;
}
for (i = 0; i < len; i += 2) {
for (let i = 0; i < len; i += 2) {
token = state.push('text', '', 0);
token.content = ch + ch;
@ -45,15 +46,12 @@ function strikethrough_tokenize(state, silent) {
function postProcess(state, delimiters) {
var i, j,
startDelim,
endDelim,
token,
loneMarkers = [],
max = delimiters.length;
let token;
const loneMarkers = [];
const max = delimiters.length;
for (i = 0; i < max; i++) {
startDelim = delimiters[i];
for (let i = 0; i < max; i++) {
const startDelim = delimiters[i];
if (startDelim.marker !== 0x7E/* ~ */) {
continue;
@ -63,7 +61,7 @@ function postProcess(state, delimiters) {
continue;
}
endDelim = delimiters[startDelim.end];
const endDelim = delimiters[startDelim.end];
token = state.tokens[startDelim.token];
token.type = 's_open';
@ -93,8 +91,8 @@ function postProcess(state, delimiters) {
// So, we have to move all those markers after subsequent s_close tags.
//
while (loneMarkers.length) {
i = loneMarkers.pop();
j = i + 1;
const i = loneMarkers.pop();
let j = i + 1;
while (j < state.tokens.length && state.tokens[j].type === 's_close') {
j++;
@ -114,13 +112,12 @@ function postProcess(state, delimiters) {
// Walk through delimiter list and replace text tokens with tags
//
function strikethrough_postProcess(state) {
var curr,
tokens_meta = state.tokens_meta,
max = state.tokens_meta.length;
const tokens_meta = state.tokens_meta;
const max = state.tokens_meta.length;
postProcess(state, state.delimiters);
for (curr = 0; curr < max; curr++) {
for (let curr = 0; curr < max; curr++) {
if (tokens_meta[curr] && tokens_meta[curr].delimiters) {
postProcess(state, tokens_meta[curr].delimiters);
}

2
lib/rules_inline/text.mjs

@ -40,7 +40,7 @@ function isTerminatorChar(ch) {
}
export default function text(state, silent) {
var pos = state.pos;
let pos = state.pos;
while (pos < state.posMax && !isTerminatorChar(state.src.charCodeAt(pos))) {
pos++;

15
lib/token.mjs

@ -120,13 +120,11 @@ function Token(type, tag, nesting) {
* Search attribute index by name.
**/
Token.prototype.attrIndex = function attrIndex(name) {
var attrs, i, len;
if (!this.attrs) { return -1; }
attrs = this.attrs;
const attrs = this.attrs;
for (i = 0, len = attrs.length; i < len; i++) {
for (let i = 0, len = attrs.length; i < len; i++) {
if (attrs[i][0] === name) { return i; }
}
return -1;
@ -153,8 +151,8 @@ Token.prototype.attrPush = function attrPush(attrData) {
* Set `name` attribute to `value`. Override old value if exists.
**/
Token.prototype.attrSet = function attrSet(name, value) {
var idx = this.attrIndex(name),
attrData = [ name, value ];
const idx = this.attrIndex(name),
attrData = [ name, value ];
if (idx < 0) {
this.attrPush(attrData);
@ -170,7 +168,8 @@ Token.prototype.attrSet = function attrSet(name, value) {
* Get the value of attribute `name`, or null if it does not exist.
**/
Token.prototype.attrGet = function attrGet(name) {
var idx = this.attrIndex(name), value = null;
const idx = this.attrIndex(name);
let value = null;
if (idx >= 0) {
value = this.attrs[idx][1];
}
@ -185,7 +184,7 @@ Token.prototype.attrGet = function attrGet(name) {
* exists. Useful to operate with token classes.
**/
Token.prototype.attrJoin = function attrJoin(name, value) {
var idx = this.attrIndex(name);
const idx = this.attrIndex(name);
if (idx < 0) {
this.attrPush([ name, value ]);

91
support/demo_template/index.mjs

@ -16,9 +16,9 @@ import md_sub from 'markdown-it-sub';
import md_sup from 'markdown-it-sup';
var mdHtml, mdSrc, permalink, scrollMap;
let mdHtml, mdSrc, permalink, scrollMap;
var defaults = {
const defaults = {
html: false, // Enable HTML tags in source
xhtmlOut: false, // Use '/' to close single tags (<br />)
breaks: false, // Convert '\n' in paragraphs into <br>
@ -33,7 +33,7 @@ var defaults = {
};
defaults.highlight = function (str, lang) {
var esc = mdHtml.utils.escapeHtml;
const esc = mdHtml.utils.escapeHtml;
try {
if (!defaults._highlight) {
@ -48,7 +48,7 @@ defaults.highlight = function (str, lang) {
} else if (lang === 'auto') {
var result = hljs.highlightAuto(str);
const result = hljs.highlightAuto(str);
/*eslint-disable no-console*/
console.log('highlight language: ' + result.language + ', relevance: ' + result.relevance);
@ -121,7 +121,7 @@ function mdInit() {
// - We track only headings and paragraphs on first level. That's enough.
// - Footnotes content causes jumps. Level limit filter it automatically.
function injectLineNumbers(tokens, idx, options, env, slf) {
var line;
let line;
if (tokens[idx].map && tokens[idx].level === 0) {
line = tokens[idx].map[0];
tokens[idx].attrJoin('class', 'line');
@ -142,7 +142,7 @@ function setHighlightedlContent(selector, content, lang) {
}
function updateResult() {
var source = $('.source').val();
const source = $('.source').val();
// Update only active view to avoid slowdowns
// (debug & src view with highlighting are a bit slow)
@ -182,11 +182,9 @@ function updateResult() {
// That's a bit dirty to process each line everytime, but ok for demo.
// Optimizations are required only for big texts.
function buildScrollMap() {
var i, offset, nonEmptyList, pos, a, b, lineHeightMap, linesCount,
acc, sourceLikeDiv, textarea = $('.source'),
_scrollMap;
const textarea = $('.source');
sourceLikeDiv = $('<div />').css({
const sourceLikeDiv = $('<div />').css({
position: 'absolute',
visibility: 'hidden',
height: 'auto',
@ -197,15 +195,13 @@ function buildScrollMap() {
'white-space': textarea.css('white-space')
}).appendTo('body');
offset = $('.result-html').scrollTop() - $('.result-html').offset().top;
_scrollMap = [];
nonEmptyList = [];
lineHeightMap = [];
const offset = $('.result-html').scrollTop() - $('.result-html').offset().top;
const _scrollMap = [];
const nonEmptyList = [];
const lineHeightMap = [];
acc = 0;
let acc = 0;
textarea.val().split('\n').forEach(function (str) {
var h, lh;
lineHeightMap.push(acc);
if (str.length === 0) {
@ -214,21 +210,22 @@ function buildScrollMap() {
}
sourceLikeDiv.text(str);
h = parseFloat(sourceLikeDiv.css('height'));
lh = parseFloat(sourceLikeDiv.css('line-height'));
const h = parseFloat(sourceLikeDiv.css('height'));
const lh = parseFloat(sourceLikeDiv.css('line-height'));
acc += Math.round(h / lh);
});
sourceLikeDiv.remove();
lineHeightMap.push(acc);
linesCount = acc;
const linesCount = acc;
for (i = 0; i < linesCount; i++) { _scrollMap.push(-1); }
for (let i = 0; i < linesCount; i++) { _scrollMap.push(-1); }
nonEmptyList.push(0);
_scrollMap[0] = 0;
$('.line').each(function (n, el) {
var $el = $(el), t = $el.data('line');
const $el = $(el);
let t = $el.data('line');
if (t === '') { return; }
t = lineHeightMap[t];
if (t !== 0) { nonEmptyList.push(t); }
@ -238,15 +235,15 @@ function buildScrollMap() {
nonEmptyList.push(linesCount);
_scrollMap[linesCount] = $('.result-html')[0].scrollHeight;
pos = 0;
for (i = 1; i < linesCount; i++) {
let pos = 0;
for (let i = 1; i < linesCount; i++) {
if (_scrollMap[i] !== -1) {
pos++;
continue;
}
a = nonEmptyList[pos];
b = nonEmptyList[pos + 1];
const a = nonEmptyList[pos];
const b = nonEmptyList[pos + 1];
_scrollMap[i] = Math.round((_scrollMap[b] * (i - a) + _scrollMap[a] * (b - i)) / (b - a));
}
@ -254,40 +251,36 @@ function buildScrollMap() {
}
// Synchronize scroll position from source to result
var syncResultScroll = _.debounce(function () {
var textarea = $('.source'),
lineHeight = parseFloat(textarea.css('line-height')),
lineNo, posTo;
const syncResultScroll = _.debounce(function () {
const textarea = $('.source');
const lineHeight = parseFloat(textarea.css('line-height'));
lineNo = Math.floor(textarea.scrollTop() / lineHeight);
const lineNo = Math.floor(textarea.scrollTop() / lineHeight);
if (!scrollMap) { scrollMap = buildScrollMap(); }
posTo = scrollMap[lineNo];
const posTo = scrollMap[lineNo];
$('.result-html').stop(true).animate({
scrollTop: posTo
}, 100, 'linear');
}, 50, { maxWait: 50 });
// Synchronize scroll position from result to source
var syncSrcScroll = _.debounce(function () {
var resultHtml = $('.result-html'),
scrollTop = resultHtml.scrollTop(),
textarea = $('.source'),
lineHeight = parseFloat(textarea.css('line-height')),
lines,
i,
line;
const syncSrcScroll = _.debounce(function () {
const resultHtml = $('.result-html');
const scrollTop = resultHtml.scrollTop();
const textarea = $('.source');
const lineHeight = parseFloat(textarea.css('line-height'));
if (!scrollMap) { scrollMap = buildScrollMap(); }
lines = Object.keys(scrollMap);
const lines = Object.keys(scrollMap);
if (lines.length < 1) {
return;
}
line = lines[0];
let line = lines[0];
for (i = 1; i < lines.length; i++) {
for (let i = 1; i < lines.length; i++) {
if (scrollMap[lines[i]] < scrollTop) {
line = lines[i];
continue;
@ -306,7 +299,7 @@ function loadPermalink() {
if (!location.hash) { return; }
var cfg, opts;
let cfg;
try {
@ -330,7 +323,7 @@ function loadPermalink() {
return;
}
opts = _.isObject(cfg.defaults) ? cfg.defaults : {};
const opts = _.isObject(cfg.defaults) ? cfg.defaults : {};
// copy config to defaults, but only if key exists
// and value has the same type
@ -376,16 +369,16 @@ $(function () {
_.forOwn(defaults, function (val, key) {
if (key === 'highlight') { return; }
var el = document.getElementById(key);
const el = document.getElementById(key);
if (!el) { return; }
var $el = $(el);
const $el = $(el);
if (_.isBoolean(val)) {
$el.prop('checked', val);
$el.on('change', function () {
var value = Boolean($el.prop('checked'));
const value = Boolean($el.prop('checked'));
setOptionClass(key, value);
defaults[key] = value;
mdInit();
@ -428,7 +421,7 @@ $(function () {
});
$(document).on('click', '[data-result-as]', function (event) {
var view = $(this).data('resultAs');
const view = $(this).data('resultAs');
if (view) {
setResultView(view);
// only to update permalink

22
support/specsplit.mjs

@ -9,7 +9,7 @@ import argparse from 'argparse';
import markdownit from '../index.mjs';
var cli = new argparse.ArgumentParser({
const cli = new argparse.ArgumentParser({
add_help: true
});
@ -29,7 +29,7 @@ cli.add_argument('-o', '--output', {
default: '-'
});
var options = cli.parse_args();
const options = cli.parse_args();
////////////////////////////////////////////////////////////////////////////////
@ -43,7 +43,7 @@ function readFile(filename, encoding, callback) {
if (options.file === '-') {
// read from stdin
var chunks = [];
const chunks = [];
process.stdin.on('data', function (chunk) {
chunks.push(chunk);
@ -61,8 +61,8 @@ function readFile(filename, encoding, callback) {
////////////////////////////////////////////////////////////////////////////////
readFile(options.spec, 'utf8', function (error, input) {
var good = [], bad = [],
markdown = markdownit('commonmark');
const good = [], bad = [],
markdown = markdownit('commonmark');
if (error) {
if (error.code === 'ENOENT') {
@ -82,11 +82,11 @@ readFile(options.spec, 'utf8', function (error, input) {
token.info.trim() === 'example';
})
.forEach(function (token) {
var arr = token.content.split(/^\.\s*?$/m, 2);
var md = arr[0];
var html = arr[1].replace(/^\n/, '');
const arr = token.content.split(/^\.\s*?$/m, 2);
const md = arr[0];
const html = arr[1].replace(/^\n/, '');
var result = {
const result = {
md: md,
html: html,
line: token.map[0],
@ -106,12 +106,12 @@ readFile(options.spec, 'utf8', function (error, input) {
}
});
var out = [];
const out = [];
if (!options.type) {
out.push(`CM spec stat: passed samples - ${good.length}, failed samples - ${bad.length}`);
} else {
var data = options.type === 'good' ? good : bad;
const data = options.type === 'good' ? good : bad;
data.forEach(function (sample) {
out.push(

6
test/babelmark-responder.mjs

@ -5,10 +5,10 @@ import { setTimeout as sleep } from 'node:timers/promises';
describe('babelmark responder app', function () {
var app;
let app;
var PORT = 5005;
var request = supertest('http://127.0.0.1:' + PORT);
const PORT = 5005;
const request = supertest('http://127.0.0.1:' + PORT);
before(async () => {
app = execFile(

4
test/commonmark.mjs

@ -14,7 +14,7 @@ function generate(path, md) {
load(path, function (data) {
data.meta = data.meta || {};
var desc = data.meta.desc || relative(path, data.file);
const desc = data.meta.desc || relative(path, data.file);
(data.meta.skip ? describe.skip : describe)(desc, function () {
data.fixtures.forEach(function (fixture) {
@ -28,7 +28,7 @@ function generate(path, md) {
describe('CommonMark', function () {
var md = markdownit('commonmark');
const md = markdownit('commonmark');
generate(fileURLToPath(new URL('fixtures/commonmark/good.txt', import.meta.url)), md);
});

2
test/markdown-it.mjs

@ -4,7 +4,7 @@ import markdownit from '../index.mjs';
describe('markdown-it', function () {
var md = markdownit({
const md = markdownit({
html: true,
langPrefix: '',
typographer: true,

98
test/misc.mjs

@ -11,12 +11,12 @@ describe('API', function () {
});
// options should override preset
var md = markdownit('commonmark', { html: false });
const md = markdownit('commonmark', { html: false });
assert.strictEqual(md.render('<!-- -->'), '<p>&lt;!-- --&gt;</p>\n');
});
it('configure coverage', function () {
var md = markdownit();
const md = markdownit();
// conditions coverage
md.configure({});
@ -28,11 +28,11 @@ describe('API', function () {
});
it('plugin', function () {
var succeeded = false;
let succeeded = false;
function plugin(slf, opts) { if (opts === 'bar') { succeeded = true; } }
var md = markdownit();
const md = markdownit();
md.use(plugin, 'foo');
assert.strictEqual(succeeded, false);
@ -41,7 +41,7 @@ describe('API', function () {
});
it('highlight', function () {
var md = markdownit({
const md = markdownit({
highlight: function (str) {
return '<pre><code>==' + str + '==</code></pre>';
}
@ -51,7 +51,7 @@ describe('API', function () {
});
it('highlight escape by default', function () {
var md = markdownit({
const md = markdownit({
highlight: function () {
return '';
}
@ -61,7 +61,7 @@ describe('API', function () {
});
it('highlight arguments', function () {
var md = markdownit({
const md = markdownit({
highlight: function (str, lang, attrs) {
assert.strictEqual(lang, 'a');
assert.strictEqual(attrs, 'b c d');
@ -73,7 +73,7 @@ describe('API', function () {
});
it('force hardbreaks', function () {
var md = markdownit({ breaks: true });
const md = markdownit({ breaks: true });
assert.strictEqual(md.render('a\nb'), '<p>a<br>\nb</p>\n');
md.set({ xhtmlOut: true });
@ -81,7 +81,7 @@ describe('API', function () {
});
it('xhtmlOut enabled', function () {
var md = markdownit({ xhtmlOut: true });
const md = markdownit({ xhtmlOut: true });
assert.strictEqual(md.render('---'), '<hr />\n');
assert.strictEqual(md.render('![]()'), '<p><img src="" alt="" /></p>\n');
@ -89,7 +89,7 @@ describe('API', function () {
});
it('xhtmlOut disabled', function () {
var md = markdownit();
const md = markdownit();
assert.strictEqual(md.render('---'), '<hr>\n');
assert.strictEqual(md.render('![]()'), '<p><img src="" alt=""></p>\n');
@ -97,9 +97,9 @@ describe('API', function () {
});
it('bulk enable/disable rules in different chains', function () {
var md = markdownit();
const md = markdownit();
var was = {
const was = {
core: md.core.ruler.getRules('').length,
block: md.block.ruler.getRules('').length,
inline: md.inline.ruler.getRules('').length
@ -108,7 +108,7 @@ describe('API', function () {
// Disable 2 rule in each chain & compare result
md.disable([ 'block', 'inline', 'code', 'fence', 'emphasis', 'entity' ]);
var now = {
const now = {
core: md.core.ruler.getRules('').length + 2,
block: md.block.ruler.getRules('').length + 2,
inline: md.inline.ruler.getRules('').length + 2
@ -119,7 +119,7 @@ describe('API', function () {
// Enable the same rules back
md.enable([ 'block', 'inline', 'code', 'fence', 'emphasis', 'entity' ]);
var back = {
const back = {
core: md.core.ruler.getRules('').length,
block: md.block.ruler.getRules('').length,
inline: md.inline.ruler.getRules('').length
@ -129,7 +129,7 @@ describe('API', function () {
});
it('bulk enable/disable with errors control', function () {
var md = markdownit();
const md = markdownit();
assert.throws(function () {
md.enable([ 'link', 'code', 'invalid' ]);
@ -146,7 +146,7 @@ describe('API', function () {
});
it('bulk enable/disable should understand strings', function () {
var md = markdownit();
const md = markdownit();
md.disable('emphasis');
assert(md.renderInline('_foo_'), '_foo_');
@ -156,7 +156,7 @@ describe('API', function () {
});
it('input type check', function () {
var md = markdownit();
const md = markdownit();
assert.throws(
function () { md.render(null); },
@ -170,7 +170,7 @@ describe('API', function () {
describe('Plugins', function () {
it('should not loop infinitely if all rules are disabled', function () {
var md = markdownit();
const md = markdownit();
md.inline.ruler.enableOnly([]);
md.inline.ruler2.enableOnly([]);
@ -180,7 +180,7 @@ describe('Plugins', function () {
});
it('should not loop infinitely if inline rule doesn\'t increment pos', function () {
var md = markdownit();
const md = markdownit();
md.inline.ruler.after('text', 'custom', function (state/*, silent*/) {
if (state.src.charCodeAt(state.pos) !== 0x40/* @ */) return false;
@ -192,10 +192,10 @@ describe('Plugins', function () {
});
it('should not loop infinitely if block rule doesn\'t increment pos', function () {
var md = markdownit();
const md = markdownit();
md.block.ruler.before('paragraph', 'custom', function (state, startLine/*, endLine, silent*/) {
var pos = state.bMarks[startLine] + state.tShift[startLine];
const pos = state.bMarks[startLine] + state.tShift[startLine];
if (state.src.charCodeAt(pos) !== 0x40/* @ */) return false;
return true;
}, { alt: [ 'paragraph' ] });
@ -209,13 +209,13 @@ describe('Plugins', function () {
describe('Misc', function () {
it('Should replace NULL characters', function () {
var md = markdownit();
const md = markdownit();
assert.strictEqual(md.render('foo\u0000bar'), '<p>foo\uFFFDbar</p>\n');
});
it('Should correctly parse strings without tailing \\n', function () {
var md = markdownit();
const md = markdownit();
assert.strictEqual(md.render('123'), '<p>123</p>\n');
assert.strictEqual(md.render('123\n'), '<p>123</p>\n');
@ -225,19 +225,19 @@ describe('Misc', function () {
});
it('Should quickly exit on empty string', function () {
var md = markdownit();
const md = markdownit();
assert.strictEqual(md.render(''), '');
});
it('Should parse inlines only', function () {
var md = markdownit();
const md = markdownit();
assert.strictEqual(md.renderInline('a *b* c'), 'a <em>b</em> c');
});
it('Renderer should have pluggable inline and block rules', function () {
var md = markdownit();
const md = markdownit();
md.renderer.rules.em_open = function () { return '<it>'; };
md.renderer.rules.em_close = function () { return '</it>'; };
@ -248,7 +248,7 @@ describe('Misc', function () {
});
it('Zero preset should disable everything', function () {
var md = markdownit('zero');
const md = markdownit('zero');
assert.strictEqual(md.render('___foo___'), '<p>___foo___</p>\n');
assert.strictEqual(md.renderInline('___foo___'), '___foo___');
@ -260,13 +260,13 @@ describe('Misc', function () {
});
it('Should correctly check block termination rules when those are disabled (#13)', function () {
var md = markdownit('zero');
const md = markdownit('zero');
assert.strictEqual(md.render('foo\nbar'), '<p>foo\nbar</p>\n');
});
it('Should render link target attr', function () {
var md = markdownit()
const md = markdownit()
.use(forInline, 'target', 'link_open', function (tokens, idx) {
tokens[idx].attrs.push([ 'target', '_blank' ]);
});
@ -275,7 +275,7 @@ describe('Misc', function () {
});
it('Should normalize CR to LF', function () {
var md = markdownit();
const md = markdownit();
assert.strictEqual(
md.render('# test\r\r - hello\r - world\r'),
@ -284,7 +284,7 @@ describe('Misc', function () {
});
it('Should normalize CR+LF to LF', function () {
var md = markdownit();
const md = markdownit();
assert.strictEqual(
md.render('# test\r\n\r\n - hello\r\n - world\r\n'),
@ -293,7 +293,7 @@ describe('Misc', function () {
});
it('Should escape surrogate pairs (coverage)', function () {
var md = markdownit();
const md = markdownit();
assert.strictEqual(md.render('\\\uD835\uDC9C'), '<p>\\\uD835\uDC9C</p>\n');
assert.strictEqual(md.render('\\\uD835x'), '<p>\\\uD835x</p>\n');
@ -305,7 +305,7 @@ describe('Misc', function () {
describe('Url normalization', function () {
it('Should be overridable', function () {
var md = markdownit({ linkify: true });
const md = markdownit({ linkify: true });
md.normalizeLink = function (url) {
assert(url.match(/example\.com/), 'wrong url passed');
@ -330,7 +330,7 @@ describe('Url normalization', function () {
describe('Links validation', function () {
it('Override validator, disable everything', function () {
var md = markdownit({ linkify: true });
const md = markdownit({ linkify: true });
md.validateLink = function () { return false; };
@ -348,7 +348,7 @@ describe('Links validation', function () {
describe('maxNesting', function () {
it('Block parser should not nest above limit', function () {
var md = markdownit({ maxNesting: 2 });
const md = markdownit({ maxNesting: 2 });
assert.strictEqual(
md.render('>foo\n>>bar\n>>>baz'),
'<blockquote>\n<p>foo</p>\n<blockquote></blockquote>\n</blockquote>\n'
@ -356,7 +356,7 @@ describe('maxNesting', function () {
});
it('Inline parser should not nest above limit', function () {
var md = markdownit({ maxNesting: 1 });
const md = markdownit({ maxNesting: 1 });
assert.strictEqual(
md.render('[`foo`]()'),
'<p><a href="">`foo`</a></p>\n'
@ -364,7 +364,7 @@ describe('maxNesting', function () {
});
it('Inline nesting coverage', function () {
var md = markdownit({ maxNesting: 2 });
const md = markdownit({ maxNesting: 2 });
assert.strictEqual(
md.render('[[[[[[[[[[[[[[[[[[foo]()'),
'<p>[[[[[[[[[[[[[[[[[[foo]()</p>\n'
@ -375,7 +375,7 @@ describe('maxNesting', function () {
describe('smartquotes', function () {
var md = markdownit({
const md = markdownit({
typographer: true,
// all strings have different length to make sure
@ -408,14 +408,14 @@ describe('smartquotes', function () {
describe('Ordered list info', function () {
var md = markdownit();
const md = markdownit();
function type_filter(tokens, type) {
return tokens.filter(function (t) { return t.type === type; });
}
it('Should mark ordered list item tokens with info', function () {
var tokens = md.parse('1. Foo\n2. Bar\n20. Fuzz');
let tokens = md.parse('1. Foo\n2. Bar\n20. Fuzz');
assert.strictEqual(type_filter(tokens, 'ordered_list_open').length, 1);
tokens = type_filter(tokens, 'list_item_open');
assert.strictEqual(tokens.length, 3);
@ -444,10 +444,10 @@ describe('Ordered list info', function () {
describe('Token attributes', function () {
it('.attrJoin', function () {
var md = markdownit();
const md = markdownit();
var tokens = md.parse('```'),
t = tokens[0];
const tokens = md.parse('```'),
t = tokens[0];
t.attrJoin('class', 'foo');
t.attrJoin('class', 'bar');
@ -459,10 +459,10 @@ describe('Token attributes', function () {
});
it('.attrSet', function () {
var md = markdownit();
const md = markdownit();
var tokens = md.parse('```'),
t = tokens[0];
const tokens = md.parse('```'),
t = tokens[0];
t.attrSet('class', 'foo');
@ -480,10 +480,10 @@ describe('Token attributes', function () {
});
it('.attrGet', function () {
var md = markdownit();
const md = markdownit();
var tokens = md.parse('```'),
t = tokens[0];
const tokens = md.parse('```'),
t = tokens[0];
assert.strictEqual(t.attrGet('myattr'), null);

29
test/ruler.mjs

@ -4,13 +4,13 @@ import Ruler from '../lib/ruler.mjs';
describe('Ruler', function () {
it('should replace rule (.at)', function () {
var ruler = new Ruler();
var res = 0;
const ruler = new Ruler();
let res = 0;
ruler.push('test', function foo() { res = 1; });
ruler.at('test', function bar() { res = 2; });
var rules = ruler.getRules('');
const rules = ruler.getRules('');
assert.strictEqual(rules.length, 1);
rules[0]();
@ -19,14 +19,14 @@ describe('Ruler', function () {
it('should inject before/after rule', function () {
var ruler = new Ruler();
var res = 0;
const ruler = new Ruler();
let res = 0;
ruler.push('test', function foo() { res = 1; });
ruler.before('test', 'before_test', function fooBefore() { res = -10; });
ruler.after('test', 'after_test', function fooAfter() { res = 10; });
var rules = ruler.getRules('');
const rules = ruler.getRules('');
assert.strictEqual(rules.length, 3);
rules[0]();
@ -39,7 +39,8 @@ describe('Ruler', function () {
it('should enable/disable rule', function () {
var rules, ruler = new Ruler();
const ruler = new Ruler();
let rules;
ruler.push('test', function foo() {});
ruler.push('test2', function bar() {});
@ -64,7 +65,8 @@ describe('Ruler', function () {
it('should enable/disable multiple rule', function () {
var rules, ruler = new Ruler();
const ruler = new Ruler();
let rules;
ruler.push('test', function foo() {});
ruler.push('test2', function bar() {});
@ -79,19 +81,20 @@ describe('Ruler', function () {
it('should enable rules by whitelist', function () {
var rules, ruler = new Ruler();
const ruler = new Ruler();
ruler.push('test', function foo() {});
ruler.push('test2', function bar() {});
ruler.enableOnly('test');
rules = ruler.getRules('');
const rules = ruler.getRules('');
assert.strictEqual(rules.length, 1);
});
it('should support multiple chains', function () {
var rules, ruler = new Ruler();
const ruler = new Ruler();
let rules;
ruler.push('test', function foo() {});
ruler.push('test2', function bar() {}, { alt: [ 'alt1' ] });
@ -107,7 +110,7 @@ describe('Ruler', function () {
it('should fail on invalid rule name', function () {
var ruler = new Ruler();
const ruler = new Ruler();
ruler.push('test', function foo() {});
@ -130,7 +133,7 @@ describe('Ruler', function () {
it('should not fail on invalid rule name in silent mode', function () {
var ruler = new Ruler();
const ruler = new Ruler();
ruler.push('test', function foo() {});

2
test/token.mjs

@ -5,7 +5,7 @@ import Token from '../lib/token.mjs';
describe('Token', function () {
it('attr', function () {
var t = new Token('test_token', 'tok', 1);
const t = new Token('test_token', 'tok', 1);
assert.strictEqual(t.attrs, null);
assert.equal(t.attrIndex('foo'), -1);

14
test/utils.mjs

@ -5,14 +5,14 @@ import utils from '../lib/common/utils.mjs';
describe('Utils', function () {
it('fromCodePoint', function () {
var fromCodePoint = utils.fromCodePoint;
const fromCodePoint = utils.fromCodePoint;
assert.strictEqual(fromCodePoint(0x20), ' ');
assert.strictEqual(fromCodePoint(0x1F601), '😁');
});
it('isValidEntityCode', function () {
var isValidEntityCode = utils.isValidEntityCode;
const isValidEntityCode = utils.isValidEntityCode;
assert.strictEqual(isValidEntityCode(0x20), true);
assert.strictEqual(isValidEntityCode(0xD800), false);
@ -38,7 +38,7 @@ describe('Utils', function () {
});*/
it('assign', function () {
var assign = utils.assign;
const assign = utils.assign;
assert.deepEqual(assign({ a: 1 }, null, { b: 2 }), { a: 1, b: 2 });
assert.throws(function () {
@ -47,13 +47,13 @@ describe('Utils', function () {
});
it('escapeRE', function () {
var escapeRE = utils.escapeRE;
const escapeRE = utils.escapeRE;
assert.strictEqual(escapeRE(' .?*+^$[]\\(){}|-'), ' \\.\\?\\*\\+\\^\\$\\[\\]\\\\\\(\\)\\{\\}\\|\\-');
});
it('isWhiteSpace', function () {
var isWhiteSpace = utils.isWhiteSpace;
const isWhiteSpace = utils.isWhiteSpace;
assert.strictEqual(isWhiteSpace(0x2000), true);
assert.strictEqual(isWhiteSpace(0x09), true);
@ -62,7 +62,7 @@ describe('Utils', function () {
});
it('isMdAsciiPunct', function () {
var isMdAsciiPunct = utils.isMdAsciiPunct;
const isMdAsciiPunct = utils.isMdAsciiPunct;
assert.strictEqual(isMdAsciiPunct(0x30), false);
@ -72,7 +72,7 @@ describe('Utils', function () {
});
it('unescapeMd', function () {
var unescapeMd = utils.unescapeMd;
const unescapeMd = utils.unescapeMd;
assert.strictEqual(unescapeMd('\\foo'), '\\foo');
assert.strictEqual(unescapeMd('foo'), 'foo');

Loading…
Cancel
Save