quikdown 1.0.4 → 1.1.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/README.md +124 -499
- package/dist/quikdown.cjs +130 -48
- package/dist/quikdown.d.ts +40 -5
- package/dist/quikdown.dark.css +1 -1
- package/dist/quikdown.esm.js +130 -48
- package/dist/quikdown.esm.min.js +2 -2
- package/dist/quikdown.esm.min.js.map +1 -1
- package/dist/quikdown.light.css +1 -1
- package/dist/quikdown.umd.js +130 -48
- package/dist/quikdown.umd.min.js +2 -2
- package/dist/quikdown.umd.min.js.map +1 -1
- package/dist/quikdown_bd.cjs +652 -260
- package/dist/quikdown_bd.d.ts +53 -19
- package/dist/quikdown_bd.esm.js +652 -260
- package/dist/quikdown_bd.esm.min.js +2 -2
- package/dist/quikdown_bd.esm.min.js.map +1 -1
- package/dist/quikdown_bd.umd.js +652 -260
- package/dist/quikdown_bd.umd.min.js +2 -2
- package/dist/quikdown_bd.umd.min.js.map +1 -1
- package/dist/quikdown_edit.cjs +2474 -0
- package/dist/quikdown_edit.d.ts +195 -0
- package/dist/quikdown_edit.esm.js +2472 -0
- package/dist/quikdown_edit.esm.min.js +14 -0
- package/dist/quikdown_edit.esm.min.js.map +1 -0
- package/dist/quikdown_edit.umd.js +2480 -0
- package/dist/quikdown_edit.umd.min.js +14 -0
- package/dist/quikdown_edit.umd.min.js.map +1 -0
- package/package.json +71 -29
- package/dist/quikdown-lex.cjs +0 -810
- package/dist/quikdown-lex.esm.js +0 -808
- package/dist/quikdown-lex.esm.min.js +0 -8
- package/dist/quikdown-lex.esm.min.js.map +0 -1
- package/dist/quikdown-lex.umd.js +0 -816
- package/dist/quikdown-lex.umd.min.js +0 -8
- package/dist/quikdown-lex.umd.min.js.map +0 -1
package/dist/quikdown.light.css
CHANGED
package/dist/quikdown.umd.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* quikdown - Lightweight Markdown Parser
|
|
3
|
-
* @version 1.0
|
|
3
|
+
* @version 1.1.0
|
|
4
4
|
* @license BSD-2-Clause
|
|
5
5
|
* @copyright DeftIO 2025
|
|
6
6
|
*/
|
|
@@ -18,11 +18,13 @@
|
|
|
18
18
|
* @param {Function} options.fence_plugin - Custom renderer for fenced code blocks
|
|
19
19
|
* (content, fence_string) => html string
|
|
20
20
|
* @param {boolean} options.inline_styles - If true, uses inline styles instead of classes
|
|
21
|
+
* @param {boolean} options.bidirectional - If true, adds data-qd attributes for source tracking
|
|
22
|
+
* @param {boolean} options.lazy_linefeeds - If true, single newlines become <br> tags
|
|
21
23
|
* @returns {string} - The rendered HTML
|
|
22
24
|
*/
|
|
23
25
|
|
|
24
26
|
// Version will be injected at build time
|
|
25
|
-
const quikdownVersion = '1.0
|
|
27
|
+
const quikdownVersion = '1.1.0';
|
|
26
28
|
|
|
27
29
|
// Constants for reuse
|
|
28
30
|
const CLASS_PREFIX = 'quikdown-';
|
|
@@ -64,12 +66,25 @@
|
|
|
64
66
|
function createGetAttr(inline_styles, styles) {
|
|
65
67
|
return function(tag, additionalStyle = '') {
|
|
66
68
|
if (inline_styles) {
|
|
67
|
-
|
|
69
|
+
let style = styles[tag];
|
|
68
70
|
if (!style && !additionalStyle) return '';
|
|
69
|
-
|
|
71
|
+
|
|
72
|
+
// Remove default text-align if we're adding a different alignment
|
|
73
|
+
if (additionalStyle && additionalStyle.includes('text-align') && style && style.includes('text-align')) {
|
|
74
|
+
style = style.replace(/text-align:[^;]+;?/, '').trim();
|
|
75
|
+
if (style && !style.endsWith(';')) style += ';';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* istanbul ignore next - defensive: additionalStyle without style doesn't occur with current tags */
|
|
79
|
+
const fullStyle = additionalStyle ? (style ? `${style}${additionalStyle}` : additionalStyle) : style;
|
|
70
80
|
return ` style="${fullStyle}"`;
|
|
71
81
|
} else {
|
|
72
|
-
|
|
82
|
+
const classAttr = ` class="${CLASS_PREFIX}${tag}"`;
|
|
83
|
+
// Apply inline styles for alignment even when using CSS classes
|
|
84
|
+
if (additionalStyle) {
|
|
85
|
+
return `${classAttr} style="${additionalStyle}"`;
|
|
86
|
+
}
|
|
87
|
+
return classAttr;
|
|
73
88
|
}
|
|
74
89
|
};
|
|
75
90
|
}
|
|
@@ -79,7 +94,7 @@
|
|
|
79
94
|
return '';
|
|
80
95
|
}
|
|
81
96
|
|
|
82
|
-
const { fence_plugin, inline_styles = false } = options;
|
|
97
|
+
const { fence_plugin, inline_styles = false, bidirectional = false, lazy_linefeeds = false } = options;
|
|
83
98
|
const styles = QUIKDOWN_STYLES; // Use module-level styles
|
|
84
99
|
const getAttr = createGetAttr(inline_styles, styles); // Create getAttr once
|
|
85
100
|
|
|
@@ -88,8 +103,12 @@
|
|
|
88
103
|
return text.replace(/[&<>"']/g, m => ESC_MAP[m]);
|
|
89
104
|
}
|
|
90
105
|
|
|
106
|
+
// Helper to add data-qd attributes for bidirectional support
|
|
107
|
+
const dataQd = bidirectional ? (marker) => ` data-qd="${escapeHtml(marker)}"` : () => '';
|
|
108
|
+
|
|
91
109
|
// Sanitize URLs to prevent XSS attacks
|
|
92
110
|
function sanitizeUrl(url, allowUnsafe = false) {
|
|
111
|
+
/* istanbul ignore next - defensive programming, regex ensures url is never empty */
|
|
93
112
|
if (!url) return '';
|
|
94
113
|
|
|
95
114
|
// If unsafe URLs are explicitly allowed, return as-is
|
|
@@ -131,18 +150,21 @@
|
|
|
131
150
|
// Trim the language specification
|
|
132
151
|
const langTrimmed = lang ? lang.trim() : '';
|
|
133
152
|
|
|
134
|
-
// If custom fence plugin is provided, use it
|
|
135
|
-
if (fence_plugin && typeof fence_plugin === 'function') {
|
|
153
|
+
// If custom fence plugin is provided, use it (v1.1.0: object format required)
|
|
154
|
+
if (fence_plugin && fence_plugin.render && typeof fence_plugin.render === 'function') {
|
|
136
155
|
codeBlocks.push({
|
|
137
156
|
lang: langTrimmed,
|
|
138
157
|
code: code.trimEnd(),
|
|
139
|
-
custom: true
|
|
158
|
+
custom: true,
|
|
159
|
+
fence: fence,
|
|
160
|
+
hasReverse: !!fence_plugin.reverse
|
|
140
161
|
});
|
|
141
162
|
} else {
|
|
142
163
|
codeBlocks.push({
|
|
143
164
|
lang: langTrimmed,
|
|
144
165
|
code: escapeHtml(code.trimEnd()),
|
|
145
|
-
custom: false
|
|
166
|
+
custom: false,
|
|
167
|
+
fence: fence
|
|
146
168
|
});
|
|
147
169
|
}
|
|
148
170
|
return placeholder;
|
|
@@ -166,7 +188,7 @@
|
|
|
166
188
|
// Process headings (supports optional trailing #'s)
|
|
167
189
|
html = html.replace(/^(#{1,6})\s+(.+?)\s*#*$/gm, (match, hashes, content) => {
|
|
168
190
|
const level = hashes.length;
|
|
169
|
-
return `<h${level}${getAttr('h' + level)}>${content}</h${level}>`;
|
|
191
|
+
return `<h${level}${getAttr('h' + level)}${dataQd(hashes)}>${content}</h${level}>`;
|
|
170
192
|
});
|
|
171
193
|
|
|
172
194
|
// Process blockquotes (must handle escaped > since we already escaped HTML)
|
|
@@ -174,18 +196,20 @@
|
|
|
174
196
|
// Merge consecutive blockquotes
|
|
175
197
|
html = html.replace(/<\/blockquote>\n<blockquote>/g, '\n');
|
|
176
198
|
|
|
177
|
-
// Process horizontal rules
|
|
178
|
-
html = html.replace(
|
|
199
|
+
// Process horizontal rules (allow trailing spaces)
|
|
200
|
+
html = html.replace(/^---+\s*$/gm, `<hr${getAttr('hr')}>`);
|
|
179
201
|
|
|
180
202
|
// Process lists
|
|
181
|
-
html = processLists(html, getAttr, inline_styles);
|
|
203
|
+
html = processLists(html, getAttr, inline_styles, bidirectional);
|
|
182
204
|
|
|
183
205
|
// Phase 3: Process inline elements
|
|
184
206
|
|
|
185
207
|
// Images (must come before links, with URL sanitization)
|
|
186
208
|
html = html.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (match, alt, src) => {
|
|
187
209
|
const sanitizedSrc = sanitizeUrl(src, options.allow_unsafe_urls);
|
|
188
|
-
|
|
210
|
+
const altAttr = bidirectional && alt ? ` data-qd-alt="${escapeHtml(alt)}"` : '';
|
|
211
|
+
const srcAttr = bidirectional ? ` data-qd-src="${escapeHtml(src)}"` : '';
|
|
212
|
+
return `<img${getAttr('img')} src="${sanitizedSrc}" alt="${alt}"${altAttr}${srcAttr}${dataQd('!')}>`;
|
|
189
213
|
});
|
|
190
214
|
|
|
191
215
|
// Links (with URL sanitization)
|
|
@@ -194,7 +218,8 @@
|
|
|
194
218
|
const sanitizedHref = sanitizeUrl(href, options.allow_unsafe_urls);
|
|
195
219
|
const isExternal = /^https?:\/\//i.test(sanitizedHref);
|
|
196
220
|
const rel = isExternal ? ' rel="noopener noreferrer"' : '';
|
|
197
|
-
|
|
221
|
+
const textAttr = bidirectional ? ` data-qd-text="${escapeHtml(text)}"` : '';
|
|
222
|
+
return `<a${getAttr('a')} href="${sanitizedHref}"${rel}${textAttr}${dataQd('[')}>${text}</a>`;
|
|
198
223
|
});
|
|
199
224
|
|
|
200
225
|
// Autolinks - convert bare URLs to clickable links
|
|
@@ -205,23 +230,64 @@
|
|
|
205
230
|
|
|
206
231
|
// Process inline formatting (bold, italic, strikethrough)
|
|
207
232
|
const inlinePatterns = [
|
|
208
|
-
[/\*\*(.+?)\*\*/g, 'strong'],
|
|
209
|
-
[/__(.+?)__/g, 'strong'],
|
|
210
|
-
[/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, 'em'],
|
|
211
|
-
[/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g, 'em'],
|
|
212
|
-
[/~~(.+?)~~/g, 'del']
|
|
233
|
+
[/\*\*(.+?)\*\*/g, 'strong', '**'],
|
|
234
|
+
[/__(.+?)__/g, 'strong', '__'],
|
|
235
|
+
[/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, 'em', '*'],
|
|
236
|
+
[/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g, 'em', '_'],
|
|
237
|
+
[/~~(.+?)~~/g, 'del', '~~']
|
|
213
238
|
];
|
|
214
239
|
|
|
215
|
-
inlinePatterns.forEach(([pattern, tag]) => {
|
|
216
|
-
html = html.replace(pattern, `<${tag}${getAttr(tag)}>$1</${tag}>`);
|
|
240
|
+
inlinePatterns.forEach(([pattern, tag, marker]) => {
|
|
241
|
+
html = html.replace(pattern, `<${tag}${getAttr(tag)}${dataQd(marker)}>$1</${tag}>`);
|
|
217
242
|
});
|
|
218
243
|
|
|
219
|
-
// Line breaks
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
244
|
+
// Line breaks
|
|
245
|
+
if (lazy_linefeeds) {
|
|
246
|
+
// Lazy linefeeds: single newline becomes <br> (except between paragraphs and after/before block elements)
|
|
247
|
+
const blocks = [];
|
|
248
|
+
let bi = 0;
|
|
249
|
+
|
|
250
|
+
// Protect tables and lists
|
|
251
|
+
html = html.replace(/<(table|[uo]l)[^>]*>[\s\S]*?<\/\1>/g, m => {
|
|
252
|
+
blocks[bi] = m;
|
|
253
|
+
return `§B${bi++}§`;
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// Handle paragraphs and block elements
|
|
257
|
+
html = html.replace(/\n\n+/g, '§P§')
|
|
258
|
+
// After block elements
|
|
259
|
+
.replace(/(<\/(?:h[1-6]|blockquote|pre)>)\n/g, '$1§N§')
|
|
260
|
+
.replace(/(<(?:h[1-6]|blockquote|pre|hr)[^>]*>)\n/g, '$1§N§')
|
|
261
|
+
// Before block elements
|
|
262
|
+
.replace(/\n(<(?:h[1-6]|blockquote|pre|hr)[^>]*>)/g, '§N§$1')
|
|
263
|
+
.replace(/\n(§B\d+§)/g, '§N§$1')
|
|
264
|
+
.replace(/(§B\d+§)\n/g, '$1§N§')
|
|
265
|
+
// Convert remaining newlines
|
|
266
|
+
.replace(/\n/g, `<br${getAttr('br')}>`)
|
|
267
|
+
// Restore
|
|
268
|
+
.replace(/§N§/g, '\n')
|
|
269
|
+
.replace(/§P§/g, '</p><p>');
|
|
270
|
+
|
|
271
|
+
// Restore protected blocks
|
|
272
|
+
blocks.forEach((b, i) => html = html.replace(`§B${i}§`, b));
|
|
273
|
+
|
|
274
|
+
html = '<p>' + html + '</p>';
|
|
275
|
+
} else {
|
|
276
|
+
// Standard: two spaces at end of line for line breaks
|
|
277
|
+
html = html.replace(/ $/gm, `<br${getAttr('br')}>`);
|
|
278
|
+
|
|
279
|
+
// Paragraphs (double newlines)
|
|
280
|
+
// Don't add </p> after block elements (they're not in paragraphs)
|
|
281
|
+
html = html.replace(/\n\n+/g, (match, offset) => {
|
|
282
|
+
// Check if we're after a block element closing tag
|
|
283
|
+
const before = html.substring(0, offset);
|
|
284
|
+
if (before.match(/<\/(h[1-6]|blockquote|ul|ol|table|pre|hr)>$/)) {
|
|
285
|
+
return '<p>'; // Just open a new paragraph
|
|
286
|
+
}
|
|
287
|
+
return '</p><p>'; // Normal paragraph break
|
|
288
|
+
});
|
|
289
|
+
html = '<p>' + html + '</p>';
|
|
290
|
+
}
|
|
225
291
|
|
|
226
292
|
// Clean up empty paragraphs and unwrap block elements
|
|
227
293
|
const cleanupPatterns = [
|
|
@@ -244,26 +310,39 @@
|
|
|
244
310
|
html = html.replace(pattern, replacement);
|
|
245
311
|
});
|
|
246
312
|
|
|
313
|
+
// Fix orphaned closing </p> tags after block elements
|
|
314
|
+
// When a paragraph follows a block element, ensure it has opening <p>
|
|
315
|
+
html = html.replace(/(<\/(?:h[1-6]|blockquote|ul|ol|table|pre|hr)>)\n([^<])/g, '$1\n<p>$2');
|
|
316
|
+
|
|
247
317
|
// Phase 4: Restore code blocks and inline code
|
|
248
318
|
|
|
249
319
|
// Restore code blocks
|
|
250
320
|
codeBlocks.forEach((block, i) => {
|
|
251
321
|
let replacement;
|
|
252
322
|
|
|
253
|
-
if (block.custom && fence_plugin) {
|
|
254
|
-
// Use custom fence plugin
|
|
255
|
-
replacement = fence_plugin(block.code, block.lang);
|
|
323
|
+
if (block.custom && fence_plugin && fence_plugin.render) {
|
|
324
|
+
// Use custom fence plugin (v1.1.0: object format with render function)
|
|
325
|
+
replacement = fence_plugin.render(block.code, block.lang);
|
|
326
|
+
|
|
256
327
|
// If plugin returns undefined, fall back to default rendering
|
|
257
328
|
if (replacement === undefined) {
|
|
258
329
|
const langClass = !inline_styles && block.lang ? ` class="language-${block.lang}"` : '';
|
|
259
330
|
const codeAttr = inline_styles ? getAttr('code') : langClass;
|
|
260
|
-
|
|
331
|
+
const langAttr = bidirectional && block.lang ? ` data-qd-lang="${escapeHtml(block.lang)}"` : '';
|
|
332
|
+
const fenceAttr = bidirectional ? ` data-qd-fence="${escapeHtml(block.fence)}"` : '';
|
|
333
|
+
replacement = `<pre${getAttr('pre')}${fenceAttr}${langAttr}><code${codeAttr}>${escapeHtml(block.code)}</code></pre>`;
|
|
334
|
+
} else if (bidirectional) {
|
|
335
|
+
// If bidirectional and plugin provided HTML, add data attributes for roundtrip
|
|
336
|
+
replacement = replacement.replace(/^<(\w+)/,
|
|
337
|
+
`<$1 data-qd-fence="${escapeHtml(block.fence)}" data-qd-lang="${escapeHtml(block.lang)}" data-qd-source="${escapeHtml(block.code)}"`);
|
|
261
338
|
}
|
|
262
339
|
} else {
|
|
263
340
|
// Default rendering
|
|
264
341
|
const langClass = !inline_styles && block.lang ? ` class="language-${block.lang}"` : '';
|
|
265
342
|
const codeAttr = inline_styles ? getAttr('code') : langClass;
|
|
266
|
-
|
|
343
|
+
const langAttr = bidirectional && block.lang ? ` data-qd-lang="${escapeHtml(block.lang)}"` : '';
|
|
344
|
+
const fenceAttr = bidirectional ? ` data-qd-fence="${escapeHtml(block.fence)}"` : '';
|
|
345
|
+
replacement = `<pre${getAttr('pre')}${fenceAttr}${langAttr}><code${codeAttr}>${block.code}</code></pre>`;
|
|
267
346
|
}
|
|
268
347
|
|
|
269
348
|
const placeholder = `${PLACEHOLDER_CB}${i}§`;
|
|
@@ -273,7 +352,7 @@
|
|
|
273
352
|
// Restore inline code
|
|
274
353
|
inlineCodes.forEach((code, i) => {
|
|
275
354
|
const placeholder = `${PLACEHOLDER_IC}${i}§`;
|
|
276
|
-
html = html.replace(placeholder, `<code${getAttr('code')}>${code}</code>`);
|
|
355
|
+
html = html.replace(placeholder, `<code${getAttr('code')}${dataQd('`')}>${code}</code>`);
|
|
277
356
|
});
|
|
278
357
|
|
|
279
358
|
return html.trim();
|
|
@@ -387,9 +466,9 @@
|
|
|
387
466
|
let html = `<table${getAttr('table')}>\n`;
|
|
388
467
|
|
|
389
468
|
// Build header
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
469
|
+
// Note: headerLines will always have length > 0 since separatorIndex starts from 1
|
|
470
|
+
html += `<thead${getAttr('thead')}>\n`;
|
|
471
|
+
headerLines.forEach(line => {
|
|
393
472
|
html += `<tr${getAttr('tr')}>\n`;
|
|
394
473
|
// Handle pipes at start/end or not
|
|
395
474
|
const cells = line.trim().replace(/^\|/, '').replace(/\|$/, '').split('|');
|
|
@@ -399,9 +478,8 @@
|
|
|
399
478
|
html += `<th${getAttr('th', alignStyle)}>${processedCell}</th>\n`;
|
|
400
479
|
});
|
|
401
480
|
html += '</tr>\n';
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
}
|
|
481
|
+
});
|
|
482
|
+
html += '</thead>\n';
|
|
405
483
|
|
|
406
484
|
// Build body
|
|
407
485
|
if (bodyLines.length > 0) {
|
|
@@ -427,12 +505,16 @@
|
|
|
427
505
|
/**
|
|
428
506
|
* Process markdown lists (ordered and unordered)
|
|
429
507
|
*/
|
|
430
|
-
function processLists(text, getAttr, inline_styles) {
|
|
508
|
+
function processLists(text, getAttr, inline_styles, bidirectional) {
|
|
431
509
|
|
|
432
510
|
const lines = text.split('\n');
|
|
433
511
|
const result = [];
|
|
434
512
|
let listStack = []; // Track nested lists
|
|
435
513
|
|
|
514
|
+
// Helper to escape HTML for data-qd attributes
|
|
515
|
+
const escapeHtml = (text) => text.replace(/[&<>"']/g, m => ({'&':'&','<':'<','>':'>','"':'"',"'":'''})[m]);
|
|
516
|
+
const dataQd = bidirectional ? (marker) => ` data-qd="${escapeHtml(marker)}"` : () => '';
|
|
517
|
+
|
|
436
518
|
for (let i = 0; i < lines.length; i++) {
|
|
437
519
|
const line = lines[i];
|
|
438
520
|
const match = line.match(/^(\s*)([*\-+]|\d+\.)\s+(.+)$/);
|
|
@@ -480,7 +562,7 @@
|
|
|
480
562
|
}
|
|
481
563
|
|
|
482
564
|
const liAttr = taskListClass || getAttr('li');
|
|
483
|
-
result.push(`<li${liAttr}>${listItemContent}</li>`);
|
|
565
|
+
result.push(`<li${liAttr}${dataQd(marker)}>${listItemContent}</li>`);
|
|
484
566
|
} else {
|
|
485
567
|
// Not a list item, close all lists
|
|
486
568
|
while (listStack.length > 0) {
|
|
@@ -526,8 +608,7 @@
|
|
|
526
608
|
|
|
527
609
|
let css = '';
|
|
528
610
|
for (const [tag, style] of Object.entries(styles)) {
|
|
529
|
-
|
|
530
|
-
let themedStyle = style;
|
|
611
|
+
let themedStyle = style;
|
|
531
612
|
|
|
532
613
|
// Apply theme overrides if dark theme
|
|
533
614
|
if (theme === 'dark' && themeOverrides.dark) {
|
|
@@ -550,9 +631,8 @@
|
|
|
550
631
|
themedStyle += `;color:${themeOverrides.light._textColor}`;
|
|
551
632
|
}
|
|
552
633
|
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
}
|
|
634
|
+
|
|
635
|
+
css += `.${prefix}${tag} { ${themedStyle} }\n`;
|
|
556
636
|
}
|
|
557
637
|
|
|
558
638
|
return css;
|
|
@@ -575,11 +655,13 @@
|
|
|
575
655
|
quikdown.version = quikdownVersion;
|
|
576
656
|
|
|
577
657
|
// Export for both CommonJS and ES6
|
|
658
|
+
/* istanbul ignore next */
|
|
578
659
|
if (typeof module !== 'undefined' && module.exports) {
|
|
579
660
|
module.exports = quikdown;
|
|
580
661
|
}
|
|
581
662
|
|
|
582
663
|
// For browser global
|
|
664
|
+
/* istanbul ignore next */
|
|
583
665
|
if (typeof window !== 'undefined') {
|
|
584
666
|
window.quikdown = quikdown;
|
|
585
667
|
}
|
package/dist/quikdown.umd.min.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* quikdown - Lightweight Markdown Parser
|
|
3
|
-
* @version 1.0
|
|
3
|
+
* @version 1.1.0
|
|
4
4
|
* @license BSD-2-Clause
|
|
5
5
|
* @copyright DeftIO 2025
|
|
6
6
|
*/
|
|
7
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).quikdown=t()}(this,function(){"use strict";const e="quikdown-",t="§CB",n={"&":"&","<":"<",">":">",'"':""","'":"'"},
|
|
7
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).quikdown=t()}(this,function(){"use strict";const e="quikdown-",t="§CB",n={"&":"&","<":"<",">":">",'"':""","'":"'"},r={h1:"font-size:2em;font-weight:600;margin:.67em 0;text-align:left",h2:"font-size:1.5em;font-weight:600;margin:.83em 0",h3:"font-size:1.25em;font-weight:600;margin:1em 0",h4:"font-size:1em;font-weight:600;margin:1.33em 0",h5:"font-size:.875em;font-weight:600;margin:1.67em 0",h6:"font-size:.85em;font-weight:600;margin:2em 0",pre:"background:#f4f4f4;padding:10px;border-radius:4px;overflow-x:auto;margin:1em 0",code:"background:#f0f0f0;padding:2px 4px;border-radius:3px;font-family:monospace",blockquote:"border-left:4px solid #ddd;margin-left:0;padding-left:1em",table:"border-collapse:collapse;width:100%;margin:1em 0",th:"border:1px solid #ddd;padding:8px;background-color:#f2f2f2;font-weight:bold;text-align:left",td:"border:1px solid #ddd;padding:8px;text-align:left",hr:"border:none;border-top:1px solid #ddd;margin:1em 0",img:"max-width:100%;height:auto",a:"color:#06c;text-decoration:underline",strong:"font-weight:bold",em:"font-style:italic",del:"text-decoration:line-through",ul:"margin:.5em 0;padding-left:2em",ol:"margin:.5em 0;padding-left:2em",li:"margin:.25em 0","task-item":"list-style:none","task-checkbox":"margin-right:.5em"};function o(o,l={}){if(!o||"string"!=typeof o)return"";const{fence_plugin:c,inline_styles:s=!1,bidirectional:i=!1,lazy_linefeeds:p=!1}=l,d=function(t,n){return function(r,o=""){if(t){let e=n[r];return e||o?(o&&o.includes("text-align")&&e&&e.includes("text-align")&&(e=e.replace(/text-align:[^;]+;?/,"").trim(),e&&!e.endsWith(";")&&(e+=";")),` style="${o?e?`${e}${o}`:o:e}"`):""}{const t=` class="${e}${r}"`;return o?`${t} style="${o}"`:t}}}(s,r);function g(e){return e.replace(/[&<>"']/g,e=>n[e])}const $=i?e=>` data-qd="${g(e)}"`:()=>"";function f(e,t=!1){if(!e)return"";if(t)return e;const n=e.trim(),r=n.toLowerCase(),o=["javascript:","vbscript:","data:"];for(const e of o)if(r.startsWith(e))return"data:"===e&&r.startsWith("data:image/")?n:"#";return n}let u=o;const h=[],m=[];u=u.replace(/^(```|~~~)([^\n]*)\n([\s\S]*?)^\1$/gm,(e,n,r,o)=>{const l=`${t}${h.length}§`,a=r?r.trim():"";return c&&c.render&&"function"==typeof c.render?h.push({lang:a,code:o.trimEnd(),custom:!0,fence:n,hasReverse:!!c.reverse}):h.push({lang:a,code:g(o.trimEnd()),custom:!1,fence:n}),l}),u=u.replace(/`([^`]+)`/g,(e,t)=>{const n=`§IC${m.length}§`;return m.push(g(t)),n}),u=g(u),u=function(e,t){const n=e.split("\n"),r=[];let o=!1,l=[];for(let e=0;e<n.length;e++){const c=n[e].trim();if(c.includes("|")&&(c.startsWith("|")||/[^\\|]/.test(c)))o||(o=!0,l=[]),l.push(c);else{if(o){const e=a(l,t);e?r.push(e):r.push(...l),o=!1,l=[]}r.push(n[e])}}if(o&&l.length>0){const e=a(l,t);e?r.push(e):r.push(...l)}return r.join("\n")}(u,d),u=u.replace(/^(#{1,6})\s+(.+?)\s*#*$/gm,(e,t,n)=>{const r=t.length;return`<h${r}${d("h"+r)}${$(t)}>${n}</h${r}>`}),u=u.replace(/^>\s+(.+)$/gm,`<blockquote${d("blockquote")}>$1</blockquote>`),u=u.replace(/<\/blockquote>\n<blockquote>/g,"\n"),u=u.replace(/^---+\s*$/gm,`<hr${d("hr")}>`),u=function(t,n,r,o){const l=t.split("\n"),a=[];let c=[];const s=e=>e.replace(/[&<>"']/g,e=>({"&":"&","<":"<",">":">",'"':""","'":"'"}[e])),i=o?e=>` data-qd="${s(e)}"`:()=>"";for(let t=0;t<l.length;t++){const o=l[t],s=o.match(/^(\s*)([*\-+]|\d+\.)\s+(.+)$/);if(s){const[,t,o,l]=s,p=Math.floor(t.length/2),d=/^\d+\./.test(o),g=d?"ol":"ul";let $=l,f="";const u=l.match(/^\[([x ])\]\s+(.*)$/i);if(u&&!d){const[,t,n]=u,o="x"===t.toLowerCase();$=`<input type="checkbox"${r?' style="margin-right:.5em"':` class="${e}task-checkbox"`}${o?" checked":""} disabled> ${n}`,f=r?' style="list-style:none"':` class="${e}task-item"`}for(;c.length>p+1;){const e=c.pop();a.push(`</${e.type}>`)}if(c.length===p)c.push({type:g,level:p}),a.push(`<${g}${n(g)}>`);else if(c.length===p+1){const e=c[c.length-1];e.type!==g&&(a.push(`</${e.type}>`),c.pop(),c.push({type:g,level:p}),a.push(`<${g}${n(g)}>`))}const h=f||n("li");a.push(`<li${h}${i(o)}>${$}</li>`)}else{for(;c.length>0;){const e=c.pop();a.push(`</${e.type}>`)}a.push(o)}}for(;c.length>0;){const e=c.pop();a.push(`</${e.type}>`)}return a.join("\n")}(u,d,s,i),u=u.replace(/!\[([^\]]*)\]\(([^)]+)\)/g,(e,t,n)=>{const r=f(n,l.allow_unsafe_urls),o=i&&t?` data-qd-alt="${g(t)}"`:"",a=i?` data-qd-src="${g(n)}"`:"";return`<img${d("img")} src="${r}" alt="${t}"${o}${a}${$("!")}>`}),u=u.replace(/\[([^\]]+)\]\(([^)]+)\)/g,(e,t,n)=>{const r=f(n,l.allow_unsafe_urls),o=/^https?:\/\//i.test(r)?' rel="noopener noreferrer"':"",a=i?` data-qd-text="${g(t)}"`:"";return`<a${d("a")} href="${r}"${o}${a}${$("[")}>${t}</a>`}),u=u.replace(/(^|\s)(https?:\/\/[^\s<]+)/g,(e,t,n)=>{const r=f(n,l.allow_unsafe_urls);return`${t}<a${d("a")} href="${r}" rel="noopener noreferrer">${n}</a>`});if([[/\*\*(.+?)\*\*/g,"strong","**"],[/__(.+?)__/g,"strong","__"],[/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g,"em","*"],[/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g,"em","_"],[/~~(.+?)~~/g,"del","~~"]].forEach(([e,t,n])=>{u=u.replace(e,`<${t}${d(t)}${$(n)}>$1</${t}>`)}),p){const e=[];let t=0;u=u.replace(/<(table|[uo]l)[^>]*>[\s\S]*?<\/\1>/g,n=>(e[t]=n,`§B${t++}§`)),u=u.replace(/\n\n+/g,"§P§").replace(/(<\/(?:h[1-6]|blockquote|pre)>)\n/g,"$1§N§").replace(/(<(?:h[1-6]|blockquote|pre|hr)[^>]*>)\n/g,"$1§N§").replace(/\n(<(?:h[1-6]|blockquote|pre|hr)[^>]*>)/g,"§N§$1").replace(/\n(§B\d+§)/g,"§N§$1").replace(/(§B\d+§)\n/g,"$1§N§").replace(/\n/g,`<br${d("br")}>`).replace(/§N§/g,"\n").replace(/§P§/g,"</p><p>"),e.forEach((e,t)=>u=u.replace(`§B${t}§`,e)),u="<p>"+u+"</p>"}else u=u.replace(/ $/gm,`<br${d("br")}>`),u=u.replace(/\n\n+/g,(e,t)=>u.substring(0,t).match(/<\/(h[1-6]|blockquote|ul|ol|table|pre|hr)>$/)?"<p>":"</p><p>"),u="<p>"+u+"</p>";return[[/<p><\/p>/g,""],[/<p>(<h[1-6][^>]*>)/g,"$1"],[/(<\/h[1-6]>)<\/p>/g,"$1"],[/<p>(<blockquote[^>]*>)/g,"$1"],[/(<\/blockquote>)<\/p>/g,"$1"],[/<p>(<ul[^>]*>|<ol[^>]*>)/g,"$1"],[/(<\/ul>|<\/ol>)<\/p>/g,"$1"],[/<p>(<hr[^>]*>)<\/p>/g,"$1"],[/<p>(<table[^>]*>)/g,"$1"],[/(<\/table>)<\/p>/g,"$1"],[/<p>(<pre[^>]*>)/g,"$1"],[/(<\/pre>)<\/p>/g,"$1"],[new RegExp(`<p>(${t}\\d+§)</p>`,"g"),"$1"]].forEach(([e,t])=>{u=u.replace(e,t)}),u=u.replace(/(<\/(?:h[1-6]|blockquote|ul|ol|table|pre|hr)>)\n([^<])/g,"$1\n<p>$2"),h.forEach((e,n)=>{let r;if(e.custom&&c&&c.render)if(r=c.render(e.code,e.lang),void 0===r){const t=!s&&e.lang?` class="language-${e.lang}"`:"",n=s?d("code"):t,o=i&&e.lang?` data-qd-lang="${g(e.lang)}"`:"",l=i?` data-qd-fence="${g(e.fence)}"`:"";r=`<pre${d("pre")}${l}${o}><code${n}>${g(e.code)}</code></pre>`}else i&&(r=r.replace(/^<(\w+)/,`<$1 data-qd-fence="${g(e.fence)}" data-qd-lang="${g(e.lang)}" data-qd-source="${g(e.code)}"`));else{const t=!s&&e.lang?` class="language-${e.lang}"`:"",n=s?d("code"):t,o=i&&e.lang?` data-qd-lang="${g(e.lang)}"`:"",l=i?` data-qd-fence="${g(e.fence)}"`:"";r=`<pre${d("pre")}${l}${o}><code${n}>${e.code}</code></pre>`}const o=`${t}${n}§`;u=u.replace(o,r)}),m.forEach((e,t)=>{const n=`§IC${t}§`;u=u.replace(n,`<code${d("code")}${$("`")}>${e}</code>`)}),u.trim()}function l(e,t){return[[/\*\*(.+?)\*\*/g,"strong"],[/__(.+?)__/g,"strong"],[/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g,"em"],[/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g,"em"],[/~~(.+?)~~/g,"del"],[/`([^`]+)`/g,"code"]].forEach(([n,r])=>{e=e.replace(n,`<${r}${t(r)}>$1</${r}>`)}),e}function a(e,t){if(e.length<2)return null;let n=-1;for(let t=1;t<e.length;t++)if(/^\|?[\s\-:|]+\|?$/.test(e[t])&&e[t].includes("-")){n=t;break}if(-1===n)return null;const r=e.slice(0,n),o=e.slice(n+1),a=e[n].trim().replace(/^\|/,"").replace(/\|$/,"").split("|").map(e=>{const t=e.trim();return t.startsWith(":")&&t.endsWith(":")?"center":t.endsWith(":")?"right":"left"});let c=`<table${t("table")}>\n`;return c+=`<thead${t("thead")}>\n`,r.forEach(e=>{c+=`<tr${t("tr")}>\n`;e.trim().replace(/^\|/,"").replace(/\|$/,"").split("|").forEach((e,n)=>{const r=a[n]&&"left"!==a[n]?`text-align:${a[n]}`:"",o=l(e.trim(),t);c+=`<th${t("th",r)}>${o}</th>\n`}),c+="</tr>\n"}),c+="</thead>\n",o.length>0&&(c+=`<tbody${t("tbody")}>\n`,o.forEach(e=>{c+=`<tr${t("tr")}>\n`;e.trim().replace(/^\|/,"").replace(/\|$/,"").split("|").forEach((e,n)=>{const r=a[n]&&"left"!==a[n]?`text-align:${a[n]}`:"",o=l(e.trim(),t);c+=`<td${t("td",r)}>${o}</td>\n`}),c+="</tr>\n"}),c+="</tbody>\n"),c+="</table>",c}return o.emitStyles=function(e="quikdown-",t="light"){const n=r,o={"#f4f4f4":"#2a2a2a","#f0f0f0":"#2a2a2a","#f2f2f2":"#2a2a2a","#ddd":"#3a3a3a","#06c":"#6db3f2",_textColor:"#e0e0e0"},l={_textColor:"#333"};let a="";for(const[r,c]of Object.entries(n)){let n=c;if("dark"===t&&o){for(const[e,t]of Object.entries(o))e.startsWith("_")||(n=n.replace(new RegExp(e,"g"),t));["h1","h2","h3","h4","h5","h6","td","li","blockquote"].includes(r)&&(n+=`;color:${o._textColor}`)}else if("light"===t&&l){["h1","h2","h3","h4","h5","h6","td","li","blockquote"].includes(r)&&(n+=`;color:${l._textColor}`)}a+=`.${e}${r} { ${n} }\n`}return a},o.configure=function(e){return function(t){return o(t,e)}},o.version="1.1.0","undefined"!=typeof module&&module.exports&&(module.exports=o),"undefined"!=typeof window&&(window.quikdown=o),o});
|
|
8
8
|
//# sourceMappingURL=quikdown.umd.min.js.map
|