autumnnote 1.8.3 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/autumnnote.es.js +196 -27
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.umd.js +204 -27
- package/dist/autumnnote.umd.js.map +1 -1
- package/package.json +1 -2
- package/src/js/core/markdown.js +171 -36
- package/src/js/index.js +1 -1
- package/src/js/module/Clipboard.js +25 -0
- package/types/index.d.ts +1 -1
package/dist/autumnnote.umd.js
CHANGED
|
@@ -5309,7 +5309,7 @@
|
|
|
5309
5309
|
* @returns {boolean} `true` if any Markdown-like pattern is present, `false` otherwise.
|
|
5310
5310
|
*/
|
|
5311
5311
|
function isMarkdown(text) {
|
|
5312
|
-
return /^#{1,6} [^\s]|^[ \t]*[-*+] [^\s]|^[ \t]*\d+\. [^\s]|^> [^\s]|^```|^\*{2}[^*\n]+\*{2}/m.test(text);
|
|
5312
|
+
return /^#{1,6} [^\s]|^[ \t]*[-*+] [^\s]|^[ \t]*\d+\. [^\s]|^> [^\s]|^```|^\*{2}[^*\n]+\*{2}/m.test(text) || /^.+\n=+\s*$/m.test(text) || /^.+\n-{2,}\s*$/m.test(text) || /^---\s*\n(?:[\s\S]*?\n)?(?:---|\.\.\.)\s*(?:\n|$)/.test(text) || /^\|.+\|[ \t]*\n\|[ \t:|-]+\|/m.test(text);
|
|
5313
5313
|
}
|
|
5314
5314
|
/**
|
|
5315
5315
|
* Converts a Markdown string to an HTML string.
|
|
@@ -5317,7 +5317,12 @@
|
|
|
5317
5317
|
* @returns {string}
|
|
5318
5318
|
*/
|
|
5319
5319
|
function markdownToHTML(text) {
|
|
5320
|
-
|
|
5320
|
+
let lines = text.replaceAll("\r\n", "\n").replaceAll("\r", "\n").split("\n");
|
|
5321
|
+
lines = _stripFrontmatter(lines);
|
|
5322
|
+
const refs = _extractReferenceDefinitions(lines);
|
|
5323
|
+
lines = refs.clean;
|
|
5324
|
+
_linkDefs = refs.linkDefs;
|
|
5325
|
+
_footnoteIds = refs.footnoteIds;
|
|
5321
5326
|
const out = [];
|
|
5322
5327
|
let i = 0;
|
|
5323
5328
|
while (i < lines.length) {
|
|
@@ -5336,6 +5341,18 @@
|
|
|
5336
5341
|
i++;
|
|
5337
5342
|
continue;
|
|
5338
5343
|
}
|
|
5344
|
+
if (line.trim() && !/^(-{3,}|\*{3,}|_{3,})\s*$/.test(line) && !/^#{1,6} /.test(line) && i + 1 < lines.length) {
|
|
5345
|
+
if (/^=+\s*$/.test(lines[i + 1])) {
|
|
5346
|
+
out.push(`<h1>${_inline(line.trim())}</h1>`);
|
|
5347
|
+
i += 2;
|
|
5348
|
+
continue;
|
|
5349
|
+
}
|
|
5350
|
+
if (/^-{2,}\s*$/.test(lines[i + 1])) {
|
|
5351
|
+
out.push(`<h2>${_inline(line.trim())}</h2>`);
|
|
5352
|
+
i += 2;
|
|
5353
|
+
continue;
|
|
5354
|
+
}
|
|
5355
|
+
}
|
|
5339
5356
|
if (/^(-{3,}|\*{3,}|_{3,})\s*$/.test(line)) {
|
|
5340
5357
|
out.push("<hr>");
|
|
5341
5358
|
i++;
|
|
@@ -5358,30 +5375,15 @@
|
|
|
5358
5375
|
continue;
|
|
5359
5376
|
}
|
|
5360
5377
|
if (/^[-*+] /.test(line)) {
|
|
5361
|
-
const
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
while (i < lines.length && /^[-*+] /.test(lines[i])) {
|
|
5365
|
-
if (/^[-*+]\s+\[[ xX]\]\s+/.test(lines[i]) !== isChecklist) break;
|
|
5366
|
-
const content = lines[i].slice(2);
|
|
5367
|
-
if (isChecklist) {
|
|
5368
|
-
const cbMatch = /^\[([ xX])\][ \t]+/.exec(content);
|
|
5369
|
-
const cbHtml = `<input type="checkbox" contenteditable="false"${cbMatch?.[1]?.toLowerCase() === "x" ? " checked" : ""}>`;
|
|
5370
|
-
const textContent = cbMatch ? content.slice(cbMatch[0].length) : content;
|
|
5371
|
-
items.push(`<li>${cbHtml}${_inline(textContent)}</li>`);
|
|
5372
|
-
} else items.push(`<li>${_inline(content)}</li>`);
|
|
5373
|
-
i++;
|
|
5374
|
-
}
|
|
5375
|
-
out.push(`<${listTag}>${items.join("")}</${listTag.split(" ")[0]}>`);
|
|
5378
|
+
const { html: listHtml, endIdx } = _parseListBlock(lines, i);
|
|
5379
|
+
out.push(listHtml);
|
|
5380
|
+
i = endIdx;
|
|
5376
5381
|
continue;
|
|
5377
5382
|
}
|
|
5378
5383
|
if (/^\d+\. /.test(line)) {
|
|
5379
|
-
const
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
i++;
|
|
5383
|
-
}
|
|
5384
|
-
out.push(`<ol>${items.join("")}</ol>`);
|
|
5384
|
+
const { html: listHtml, endIdx } = _parseListBlock(lines, i);
|
|
5385
|
+
out.push(listHtml);
|
|
5386
|
+
i = endIdx;
|
|
5385
5387
|
continue;
|
|
5386
5388
|
}
|
|
5387
5389
|
if (line.trim() === "") {
|
|
@@ -5390,20 +5392,29 @@
|
|
|
5390
5392
|
}
|
|
5391
5393
|
if (/^\|.+\|/.test(line) && i + 1 < lines.length && /^\|[\s|:-]+\|/.test(lines[i + 1])) {
|
|
5392
5394
|
const headerCells = _parseTableRow(line);
|
|
5395
|
+
const alignments = _parseTableRow(lines[i + 1]).map((c) => {
|
|
5396
|
+
if (c.startsWith(":") && c.endsWith(":")) return "center";
|
|
5397
|
+
if (c.endsWith(":")) return "right";
|
|
5398
|
+
if (c.startsWith(":")) return "left";
|
|
5399
|
+
return null;
|
|
5400
|
+
});
|
|
5393
5401
|
i += 2;
|
|
5394
5402
|
const bodyRows = [];
|
|
5395
5403
|
while (i < lines.length && /^\|.+\|/.test(lines[i])) {
|
|
5396
5404
|
bodyRows.push(_parseTableRow(lines[i]));
|
|
5397
5405
|
i++;
|
|
5398
5406
|
}
|
|
5399
|
-
const
|
|
5400
|
-
|
|
5407
|
+
const _cell = (tag, content, align) => {
|
|
5408
|
+
return `<${tag}${align ? ` style="text-align:${align}"` : ""}>${_inline(content)}</${tag}>`;
|
|
5409
|
+
};
|
|
5410
|
+
const thead = `<thead><tr>${headerCells.map((c, idx) => _cell("th", c, alignments[idx])).join("")}</tr></thead>`;
|
|
5411
|
+
const renderRow = (row) => `<tr>${row.map((c, idx) => _cell("td", c, alignments[idx])).join("")}</tr>`;
|
|
5401
5412
|
const tbody = bodyRows.length ? `<tbody>${bodyRows.map(renderRow).join("")}</tbody>` : "";
|
|
5402
5413
|
out.push(`<table>${thead}${tbody}</table>`);
|
|
5403
5414
|
continue;
|
|
5404
5415
|
}
|
|
5405
5416
|
const paraLines = [];
|
|
5406
|
-
while (i < lines.length && lines[i].trim() !== "" && !/^(#{1,6} |> |[-*+] |\d+\. |```|---\s*$|\*{3}\s*$|_{3}\s*$)/.test(lines[i]) && !/^\|.+\|/.test(lines[i])) {
|
|
5417
|
+
while (i < lines.length && lines[i].trim() !== "" && !/^(#{1,6} |> |[-*+] |\d+\. |```|---\s*$|\*{3}\s*$|_{3}\s*$)/.test(lines[i]) && !/^\|.+\|/.test(lines[i]) && !(i + 1 < lines.length && /^=+\s*$/.test(lines[i + 1])) && !(i + 1 < lines.length && /^-{2,}\s*$/.test(lines[i + 1]))) {
|
|
5407
5418
|
paraLines.push(lines[i]);
|
|
5408
5419
|
i++;
|
|
5409
5420
|
}
|
|
@@ -5411,6 +5422,76 @@
|
|
|
5411
5422
|
}
|
|
5412
5423
|
return out.join("");
|
|
5413
5424
|
}
|
|
5425
|
+
/** Reference-link and footnote definitions collected per markdownToHTML() call. */
|
|
5426
|
+
var _linkDefs = /* @__PURE__ */ new Map();
|
|
5427
|
+
var _footnoteIds = /* @__PURE__ */ new Set();
|
|
5428
|
+
/**
|
|
5429
|
+
* Strips a leading YAML frontmatter block (--- ... --- or --- ... ...) from
|
|
5430
|
+
* the line array, only when it is the very first line and the enclosed body
|
|
5431
|
+
* looks like YAML (key: value / list items / indented continuations) — this
|
|
5432
|
+
* disambiguates real frontmatter from a horizontal rule followed by prose.
|
|
5433
|
+
* @param {string[]} lines
|
|
5434
|
+
* @returns {string[]}
|
|
5435
|
+
*/
|
|
5436
|
+
function _stripFrontmatter(lines) {
|
|
5437
|
+
if ((lines[0] || "").trim() !== "---") return lines;
|
|
5438
|
+
let closeIdx = -1;
|
|
5439
|
+
for (let j = 1; j < lines.length; j++) {
|
|
5440
|
+
const t = lines[j].trim();
|
|
5441
|
+
if (t === "---" || t === "...") {
|
|
5442
|
+
closeIdx = j;
|
|
5443
|
+
break;
|
|
5444
|
+
}
|
|
5445
|
+
}
|
|
5446
|
+
if (closeIdx === -1) return lines;
|
|
5447
|
+
if (!lines.slice(1, closeIdx).every((l) => l.trim() === "" || /^[ \t]*[\w$.-]+\s*:(\s|$)/.test(l) || /^[ \t]*-\s+\S/.test(l) || /^[ \t]+\S/.test(l))) return lines;
|
|
5448
|
+
let start = closeIdx + 1;
|
|
5449
|
+
if (lines[start] !== void 0 && lines[start].trim() === "") start++;
|
|
5450
|
+
return lines.slice(start);
|
|
5451
|
+
}
|
|
5452
|
+
/**
|
|
5453
|
+
* Extracts GFM reference-link definitions (`[ref]: url "title"`) and footnote
|
|
5454
|
+
* definitions (`[^id]: text`) from the line array, skipping fenced code
|
|
5455
|
+
* regions. Returns the definition-free line array plus lookup maps.
|
|
5456
|
+
* @param {string[]} lines
|
|
5457
|
+
* @returns {{ clean: string[], linkDefs: Map<string, {href: string, title?: string}>, footnoteIds: Set<string> }}
|
|
5458
|
+
*/
|
|
5459
|
+
function _extractReferenceDefinitions(lines) {
|
|
5460
|
+
const linkDefs = /* @__PURE__ */ new Map();
|
|
5461
|
+
const footnoteIds = /* @__PURE__ */ new Set();
|
|
5462
|
+
const clean = [];
|
|
5463
|
+
let inFence = false;
|
|
5464
|
+
const linkDefRe = /^\[([^\]]+)\]:\s*(\S+)(?:\s+"([^"]*)")?\s*$/;
|
|
5465
|
+
const footnoteDefRe = /^\[\^([^\]]+)\]:\s*(.+)$/;
|
|
5466
|
+
for (const line of lines) {
|
|
5467
|
+
if (/^```/.test(line)) {
|
|
5468
|
+
inFence = !inFence;
|
|
5469
|
+
clean.push(line);
|
|
5470
|
+
continue;
|
|
5471
|
+
}
|
|
5472
|
+
if (!inFence) {
|
|
5473
|
+
const fm = footnoteDefRe.exec(line);
|
|
5474
|
+
if (fm) {
|
|
5475
|
+
footnoteIds.add(fm[1]);
|
|
5476
|
+
continue;
|
|
5477
|
+
}
|
|
5478
|
+
const lm = linkDefRe.exec(line);
|
|
5479
|
+
if (lm) {
|
|
5480
|
+
linkDefs.set(lm[1].trim().toLowerCase(), {
|
|
5481
|
+
href: lm[2],
|
|
5482
|
+
title: lm[3]
|
|
5483
|
+
});
|
|
5484
|
+
continue;
|
|
5485
|
+
}
|
|
5486
|
+
}
|
|
5487
|
+
clean.push(line);
|
|
5488
|
+
}
|
|
5489
|
+
return {
|
|
5490
|
+
clean,
|
|
5491
|
+
linkDefs,
|
|
5492
|
+
footnoteIds
|
|
5493
|
+
};
|
|
5494
|
+
}
|
|
5414
5495
|
/**
|
|
5415
5496
|
* Splits a GFM table row string into trimmed cell strings.
|
|
5416
5497
|
* '| a | b | c |' → ['a', 'b', 'c']
|
|
@@ -5420,9 +5501,73 @@
|
|
|
5420
5501
|
function _parseTableRow(row) {
|
|
5421
5502
|
return row.replace(/^\|/, "").replace(/\|$/, "").split("|").map((c) => c.trim());
|
|
5422
5503
|
}
|
|
5504
|
+
function _parseListBlock(lines, startIdx) {
|
|
5505
|
+
const baseIndent = lines[startIdx].match(/^(\s*)/)[1].length;
|
|
5506
|
+
const isOL = /^\s*\d+\. /.test(lines[startIdx]);
|
|
5507
|
+
const items = [];
|
|
5508
|
+
let firstIsCB = null;
|
|
5509
|
+
let i = startIdx;
|
|
5510
|
+
while (i < lines.length) {
|
|
5511
|
+
const line = lines[i];
|
|
5512
|
+
if (line.trim() === "") break;
|
|
5513
|
+
const indent = line.match(/^(\s*)/)[1].length;
|
|
5514
|
+
if (indent < baseIndent) break;
|
|
5515
|
+
if (indent === baseIndent) {
|
|
5516
|
+
if (!/^\s*(?:[-*+]|\d+\.) /.test(line)) break;
|
|
5517
|
+
if (/^\s*\d+\. /.test(line) !== isOL) break;
|
|
5518
|
+
const raw = isOL ? line.replace(/^\s*\d+\. /, "") : line.replace(/^\s*[-*+] /, "");
|
|
5519
|
+
const isCB = !isOL && /^\[[ xX]\]\s+/.test(raw);
|
|
5520
|
+
if (firstIsCB === null) firstIsCB = isCB;
|
|
5521
|
+
if (isCB !== firstIsCB) break;
|
|
5522
|
+
const checked = isCB && raw[1].toLowerCase() === "x";
|
|
5523
|
+
const text = isCB ? raw.replace(/^\[[ xX]\]\s+/, "") : raw;
|
|
5524
|
+
items.push({
|
|
5525
|
+
text,
|
|
5526
|
+
isCB,
|
|
5527
|
+
checked,
|
|
5528
|
+
sub: ""
|
|
5529
|
+
});
|
|
5530
|
+
i++;
|
|
5531
|
+
} else {
|
|
5532
|
+
if (!items.length) {
|
|
5533
|
+
i++;
|
|
5534
|
+
continue;
|
|
5535
|
+
}
|
|
5536
|
+
if (/^\s*(?:[-*+]|\d+\.) /.test(line)) {
|
|
5537
|
+
const nested = _parseListBlock(lines, i);
|
|
5538
|
+
items[items.length - 1].sub += nested.html;
|
|
5539
|
+
i = nested.endIdx;
|
|
5540
|
+
} else {
|
|
5541
|
+
items[items.length - 1].text += " " + line.trim();
|
|
5542
|
+
i++;
|
|
5543
|
+
}
|
|
5544
|
+
}
|
|
5545
|
+
}
|
|
5546
|
+
const open = isOL ? "<ol>" : !isOL && firstIsCB === true ? "<ul class=\"an-checklist\">" : "<ul>";
|
|
5547
|
+
const close = isOL ? "</ol>" : "</ul>";
|
|
5548
|
+
return {
|
|
5549
|
+
html: `${open}${items.map(({ text, isCB, checked, sub }) => {
|
|
5550
|
+
return `<li>${isCB ? `<input type="checkbox" contenteditable="false"${checked ? " checked" : ""}>` : ""}${_inline(text)}${sub}</li>`;
|
|
5551
|
+
}).join("")}${close}`,
|
|
5552
|
+
endIdx: i
|
|
5553
|
+
};
|
|
5554
|
+
}
|
|
5423
5555
|
function _inline(text) {
|
|
5424
5556
|
text = text.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (_, alt, src) => `<img src="${_escAttr(src)}" alt="${_escAttr(alt)}" class="an-image">`);
|
|
5425
5557
|
text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, label, href) => `<a href="${_escAttr(href)}">${_esc(label)}</a>`);
|
|
5558
|
+
text = text.replace(/\[([^\]]+)\]\[([^\]]*)\]/g, (m, label, ref) => {
|
|
5559
|
+
const def = _linkDefs.get((ref || label).trim().toLowerCase());
|
|
5560
|
+
if (!def) return m;
|
|
5561
|
+
const titleAttr = def.title ? ` title="${_escAttr(def.title)}"` : "";
|
|
5562
|
+
return `<a href="${_escAttr(def.href)}"${titleAttr}>${_esc(label)}</a>`;
|
|
5563
|
+
});
|
|
5564
|
+
text = text.replace(/\[([^\]]+)\]/g, (m, label) => {
|
|
5565
|
+
const def = _linkDefs.get(label.trim().toLowerCase());
|
|
5566
|
+
if (!def) return m;
|
|
5567
|
+
const titleAttr = def.title ? ` title="${_escAttr(def.title)}"` : "";
|
|
5568
|
+
return `<a href="${_escAttr(def.href)}"${titleAttr}>${_esc(label)}</a>`;
|
|
5569
|
+
});
|
|
5570
|
+
text = text.replace(/\[\^([^\]]+)\]/g, (m, id) => _footnoteIds.has(id) ? `<sup>[${_esc(id)}]</sup>` : m);
|
|
5426
5571
|
text = text.replace(/\*{3}([^*\n]+?)\*{3}/g, (_, c) => `<strong><em>${_esc(c)}</em></strong>`);
|
|
5427
5572
|
text = text.replace(/_{3}([^_\n]+?)_{3}/g, (_, c) => `<strong><em>${_esc(c)}</em></strong>`);
|
|
5428
5573
|
text = text.replace(/\*{2}([^*\n]+?)\*{2}/g, (_, c) => `<strong>${_esc(c)}</strong>`);
|
|
@@ -6971,6 +7116,29 @@
|
|
|
6971
7116
|
return doc.body.innerHTML;
|
|
6972
7117
|
}
|
|
6973
7118
|
/**
|
|
7119
|
+
* Normalizes task lists from external sources (GitHub, GitLab, etc.) so they
|
|
7120
|
+
* pass the sanitiser's `ul.an-checklist` guard. Runs before sanitiseHTML().
|
|
7121
|
+
* @param {string} html
|
|
7122
|
+
* @returns {string}
|
|
7123
|
+
*/
|
|
7124
|
+
_normalizeExternalTaskLists(html) {
|
|
7125
|
+
const doc = new DOMParser().parseFromString(`<body>${html}</body>`, "text/html");
|
|
7126
|
+
for (const cb of doc.querySelectorAll("input[type=\"checkbox\"]")) {
|
|
7127
|
+
const li = cb.closest("li");
|
|
7128
|
+
const ul = li?.closest("ul");
|
|
7129
|
+
if (!li || !ul || ul.classList.contains("an-checklist")) continue;
|
|
7130
|
+
ul.classList.add("an-checklist");
|
|
7131
|
+
cb.removeAttribute("disabled");
|
|
7132
|
+
cb.setAttribute("contenteditable", "false");
|
|
7133
|
+
for (const attr of Array.from(cb.attributes)) if (![
|
|
7134
|
+
"type",
|
|
7135
|
+
"checked",
|
|
7136
|
+
"contenteditable"
|
|
7137
|
+
].includes(attr.name)) cb.removeAttribute(attr.name);
|
|
7138
|
+
}
|
|
7139
|
+
return doc.body.innerHTML;
|
|
7140
|
+
}
|
|
7141
|
+
/**
|
|
6974
7142
|
* Forces the next paste operation to strip all HTML formatting.
|
|
6975
7143
|
* Called by Editor when Ctrl+Shift+V is pressed.
|
|
6976
7144
|
* @param {boolean} val
|
|
@@ -7028,6 +7196,7 @@
|
|
|
7028
7196
|
let html = raw;
|
|
7029
7197
|
if (isWordContent) html = this._cleanWordHtml(html);
|
|
7030
7198
|
else if (isSocialContent) html = this._cleanSocialHtml(html);
|
|
7199
|
+
html = this._normalizeExternalTaskLists(html);
|
|
7031
7200
|
html = sanitiseHTML(html);
|
|
7032
7201
|
if (this.options.pasteStripAttributes) html = this._stripAttributes(html);
|
|
7033
7202
|
execCommand("insertHTML", html);
|
|
@@ -16971,6 +17140,14 @@
|
|
|
16971
17140
|
}
|
|
16972
17141
|
};
|
|
16973
17142
|
//#endregion
|
|
17143
|
+
//#region src/js/core/env.js
|
|
17144
|
+
/**
|
|
17145
|
+
* env.js - Environment / browser detection
|
|
17146
|
+
* Inspired by Summernote's env.js
|
|
17147
|
+
*/
|
|
17148
|
+
var userAgent = navigator.userAgent;
|
|
17149
|
+
/Chrome\//.test(userAgent), /Firefox\//.test(userAgent), /^((?!chrome|android).)*safari/i.test(userAgent), /Edg\//.test(userAgent), /Macintosh/.test(userAgent), /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent), "ontouchstart" in globalThis || navigator.maxTouchPoints, /Macintosh/.test(userAgent);
|
|
17150
|
+
//#endregion
|
|
16974
17151
|
//#region src/js/index.js
|
|
16975
17152
|
var _originalDefaults = { ...defaultOptions };
|
|
16976
17153
|
/** @type {WeakMap<Element, Context>} */
|
|
@@ -17079,7 +17256,7 @@
|
|
|
17079
17256
|
/** All pre-built button definitions — accessible in every module format including UMD/CJS. */
|
|
17080
17257
|
buttons,
|
|
17081
17258
|
/** Library version */
|
|
17082
|
-
version: "1.
|
|
17259
|
+
version: "1.10.0"
|
|
17083
17260
|
};
|
|
17084
17261
|
/**
|
|
17085
17262
|
* @param {string|Element|NodeList|Element[]} selector
|