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.
@@ -5,7 +5,7 @@
5
5
  * Theme with container-based scoping.
6
6
  * Usage: <div class="quikdown-light">...content...</div>
7
7
  *
8
- * @generated 2025-08-18T00:32:14.950Z
8
+ * @version 1.1.0
9
9
  * @source tools/generateThemeCSS.js
10
10
  */
11
11
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * quikdown - Lightweight Markdown Parser
3
- * @version 1.0.4
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.4';
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
- const style = styles[tag];
69
+ let style = styles[tag];
68
70
  if (!style && !additionalStyle) return '';
69
- const fullStyle = additionalStyle ? (style ? `${style};${additionalStyle}` : additionalStyle) : style;
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
- return ` class="${CLASS_PREFIX}${tag}"`;
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(/^---+$/gm, `<hr${getAttr('hr')}>`);
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
- return `<img${getAttr('img')} src="${sanitizedSrc}" alt="${alt}">`;
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
- return `<a${getAttr('a')} href="${sanitizedHref}"${rel}>${text}</a>`;
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 (two spaces at end of line)
220
- html = html.replace(/ $/gm, `<br${getAttr('br')}>`);
221
-
222
- // Paragraphs (double newlines)
223
- html = html.replace(/\n\n+/g, '</p><p>');
224
- html = '<p>' + html + '</p>';
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
- replacement = `<pre${getAttr('pre')}><code${codeAttr}>${escapeHtml(block.code)}</code></pre>`;
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
- replacement = `<pre${getAttr('pre')}><code${codeAttr}>${block.code}</code></pre>`;
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
- if (headerLines.length > 0) {
391
- html += `<thead${getAttr('thead')}>\n`;
392
- headerLines.forEach(line => {
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
- html += '</thead>\n';
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 => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'})[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
- if (style) {
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
- css += `.${prefix}${tag} { ${themedStyle} }\n`;
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
  }
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * quikdown - Lightweight Markdown Parser
3
- * @version 1.0.4
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={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},o={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 r(r,l={}){if(!r||"string"!=typeof r)return"";const{fence_plugin:s,inline_styles:c=!1}=l,a=function(t,n){return function(o,r=""){if(t){const e=n[o];return e||r?` style="${r?e?`${e};${r}`:r:e}"`:""}return` class="${e}${o}"`}}(c,o);function p(e){return e.replace(/[&<>"']/g,e=>n[e])}function f(e,t=!1){if(!e)return"";if(t)return e;const n=e.trim(),o=n.toLowerCase(),r=["javascript:","vbscript:","data:"];for(const e of r)if(o.startsWith(e))return"data:"===e&&o.startsWith("data:image/")?n:"#";return n}let g=r;const d=[],u=[];g=g.replace(/^(```|~~~)([^\n]*)\n([\s\S]*?)^\1$/gm,(e,n,o,r)=>{const l=`${t}${d.length}§`,i=o?o.trim():"";return s&&"function"==typeof s?d.push({lang:i,code:r.trimEnd(),custom:!0}):d.push({lang:i,code:p(r.trimEnd()),custom:!1}),l}),g=g.replace(/`([^`]+)`/g,(e,t)=>{const n=`§IC${u.length}§`;return u.push(p(t)),n}),g=p(g),g=function(e,t){const n=e.split("\n"),o=[];let r=!1,l=[];for(let e=0;e<n.length;e++){const s=n[e].trim();if(s.includes("|")&&(s.startsWith("|")||/[^\\|]/.test(s)))r||(r=!0,l=[]),l.push(s);else{if(r){const e=i(l,t);e?o.push(e):o.push(...l),r=!1,l=[]}o.push(n[e])}}if(r&&l.length>0){const e=i(l,t);e?o.push(e):o.push(...l)}return o.join("\n")}(g,a),g=g.replace(/^(#{1,6})\s+(.+?)\s*#*$/gm,(e,t,n)=>{const o=t.length;return`<h${o}${a("h"+o)}>${n}</h${o}>`}),g=g.replace(/^&gt;\s+(.+)$/gm,`<blockquote${a("blockquote")}>$1</blockquote>`),g=g.replace(/<\/blockquote>\n<blockquote>/g,"\n"),g=g.replace(/^---+$/gm,`<hr${a("hr")}>`),g=function(t,n,o){const r=t.split("\n"),l=[];let i=[];for(let t=0;t<r.length;t++){const s=r[t],c=s.match(/^(\s*)([*\-+]|\d+\.)\s+(.+)$/);if(c){const[,t,r,s]=c,a=Math.floor(t.length/2),p=/^\d+\./.test(r),f=p?"ol":"ul";let g=s,d="";const u=s.match(/^\[([x ])\]\s+(.*)$/i);if(u&&!p){const[,t,n]=u,r="x"===t.toLowerCase();g=`<input type="checkbox"${o?' style="margin-right:.5em"':` class="${e}task-checkbox"`}${r?" checked":""} disabled> ${n}`,d=o?' style="list-style:none"':` class="${e}task-item"`}for(;i.length>a+1;){const e=i.pop();l.push(`</${e.type}>`)}if(i.length===a)i.push({type:f,level:a}),l.push(`<${f}${n(f)}>`);else if(i.length===a+1){const e=i[i.length-1];e.type!==f&&(l.push(`</${e.type}>`),i.pop(),i.push({type:f,level:a}),l.push(`<${f}${n(f)}>`))}const h=d||n("li");l.push(`<li${h}>${g}</li>`)}else{for(;i.length>0;){const e=i.pop();l.push(`</${e.type}>`)}l.push(s)}}for(;i.length>0;){const e=i.pop();l.push(`</${e.type}>`)}return l.join("\n")}(g,a,c),g=g.replace(/!\[([^\]]*)\]\(([^)]+)\)/g,(e,t,n)=>{const o=f(n,l.allow_unsafe_urls);return`<img${a("img")} src="${o}" alt="${t}">`}),g=g.replace(/\[([^\]]+)\]\(([^)]+)\)/g,(e,t,n)=>{const o=f(n,l.allow_unsafe_urls),r=/^https?:\/\//i.test(o)?' rel="noopener noreferrer"':"";return`<a${a("a")} href="${o}"${r}>${t}</a>`}),g=g.replace(/(^|\s)(https?:\/\/[^\s<]+)/g,(e,t,n)=>{const o=f(n,l.allow_unsafe_urls);return`${t}<a${a("a")} href="${o}" rel="noopener noreferrer">${n}</a>`});[[/\*\*(.+?)\*\*/g,"strong"],[/__(.+?)__/g,"strong"],[/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g,"em"],[/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g,"em"],[/~~(.+?)~~/g,"del"]].forEach(([e,t])=>{g=g.replace(e,`<${t}${a(t)}>$1</${t}>`)}),g=g.replace(/ $/gm,`<br${a("br")}>`),g=g.replace(/\n\n+/g,"</p><p>"),g="<p>"+g+"</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])=>{g=g.replace(e,t)}),d.forEach((e,n)=>{let o;if(e.custom&&s){if(o=s(e.code,e.lang),void 0===o){const t=!c&&e.lang?` class="language-${e.lang}"`:"",n=c?a("code"):t;o=`<pre${a("pre")}><code${n}>${p(e.code)}</code></pre>`}}else{const t=!c&&e.lang?` class="language-${e.lang}"`:"",n=c?a("code"):t;o=`<pre${a("pre")}><code${n}>${e.code}</code></pre>`}const r=`${t}${n}§`;g=g.replace(r,o)}),u.forEach((e,t)=>{const n=`§IC${t}§`;g=g.replace(n,`<code${a("code")}>${e}</code>`)}),g.trim()}function l(e,t){return[[/\*\*(.+?)\*\*/g,"strong"],[/__(.+?)__/g,"strong"],[/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g,"em"],[/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g,"em"],[/~~(.+?)~~/g,"del"],[/`([^`]+)`/g,"code"]].forEach(([n,o])=>{e=e.replace(n,`<${o}${t(o)}>$1</${o}>`)}),e}function i(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 o=e.slice(0,n),r=e.slice(n+1),i=e[n].trim().replace(/^\|/,"").replace(/\|$/,"").split("|").map(e=>{const t=e.trim();return t.startsWith(":")&&t.endsWith(":")?"center":t.endsWith(":")?"right":"left"});let s=`<table${t("table")}>\n`;return o.length>0&&(s+=`<thead${t("thead")}>\n`,o.forEach(e=>{s+=`<tr${t("tr")}>\n`;e.trim().replace(/^\|/,"").replace(/\|$/,"").split("|").forEach((e,n)=>{const o=i[n]&&"left"!==i[n]?`text-align:${i[n]}`:"",r=l(e.trim(),t);s+=`<th${t("th",o)}>${r}</th>\n`}),s+="</tr>\n"}),s+="</thead>\n"),r.length>0&&(s+=`<tbody${t("tbody")}>\n`,r.forEach(e=>{s+=`<tr${t("tr")}>\n`;e.trim().replace(/^\|/,"").replace(/\|$/,"").split("|").forEach((e,n)=>{const o=i[n]&&"left"!==i[n]?`text-align:${i[n]}`:"",r=l(e.trim(),t);s+=`<td${t("td",o)}>${r}</td>\n`}),s+="</tr>\n"}),s+="</tbody>\n"),s+="</table>",s}return r.emitStyles=function(e="quikdown-",t="light"){const n=o,r={"#f4f4f4":"#2a2a2a","#f0f0f0":"#2a2a2a","#f2f2f2":"#2a2a2a","#ddd":"#3a3a3a","#06c":"#6db3f2",_textColor:"#e0e0e0"},l={_textColor:"#333"};let i="";for(const[o,s]of Object.entries(n))if(s){let n=s;if("dark"===t&&r){for(const[e,t]of Object.entries(r))e.startsWith("_")||(n=n.replace(new RegExp(e,"g"),t));["h1","h2","h3","h4","h5","h6","td","li","blockquote"].includes(o)&&(n+=`;color:${r._textColor}`)}else if("light"===t&&l){["h1","h2","h3","h4","h5","h6","td","li","blockquote"].includes(o)&&(n+=`;color:${l._textColor}`)}i+=`.${e}${o} { ${n} }\n`}return i},r.configure=function(e){return function(t){return r(t,e)}},r.version="1.0.4","undefined"!=typeof module&&module.exports&&(module.exports=r),"undefined"!=typeof window&&(window.quikdown=r),r});
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={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},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(/^&gt;\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=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}[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