@arsedizioni/ars-utils 20.0.35 → 20.0.37

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/core/index.d.ts CHANGED
@@ -203,11 +203,43 @@ declare class SystemUtils {
203
203
  */
204
204
  static replaceAsHtml(s?: string): string;
205
205
  /**
206
- * Convert a markdown string to html
207
- * @param markdown : the markdown string
208
- * @returns
206
+ * Convert markdown to html
207
+ * @param markdown : the markdown data
208
+ * @returns the html
209
209
  */
210
- static markdownToHtml(markdown?: string): string | undefined;
210
+ static markdownToHtml(markdown: string): string;
211
+ /**
212
+ * Convert markdown table to html
213
+ * @param markdown: the markdown string
214
+ * @returns : the html
215
+ */
216
+ private static markdownConvertTables;
217
+ /**
218
+ * Convert a markdown table row to html
219
+ * @param row : the markdown row
220
+ * @param isHeader : true if is header
221
+ * @returns the html
222
+ */
223
+ private static markdownConvertTableRow;
224
+ /**
225
+ * Convert a markdown list to html
226
+ * @param markdown : the markdown list
227
+ * @returns the html
228
+ */
229
+ private static markdownConvertLists;
230
+ /**
231
+ * Wrap a list
232
+ * @param type: the list type
233
+ * @param items : the item list
234
+ * @returns the html
235
+ */
236
+ private static markdownWrapList;
237
+ /**
238
+ * Convert markdown paragraphs to html
239
+ * @param markdown : the markdown paragraphs
240
+ * @returns the html
241
+ */
242
+ private static markdownConvertParagraphs;
211
243
  /**
212
244
  * Compare two names
213
245
  * @param a : name a
@@ -857,6 +889,14 @@ declare class RemoveFocusDirective {
857
889
  static ɵdir: i0.ɵɵDirectiveDeclaration<RemoveFocusDirective, "[removeFocus]", never, {}, {}, never, never, true, never>;
858
890
  }
859
891
 
892
+ declare class FormatMarkdownPipe implements PipeTransform {
893
+ private sanitizer;
894
+ transform(value?: string): SafeHtml;
895
+ static ɵfac: i0.ɵɵFactoryDeclaration<FormatMarkdownPipe, never>;
896
+ static ɵpipe: i0.ɵɵPipeDeclaration<FormatMarkdownPipe, "formatMarkdown", true>;
897
+ static ɵprov: i0.ɵɵInjectableDeclaration<FormatMarkdownPipe>;
898
+ }
899
+
860
900
  /**
861
901
  * Standard Broadcast Messages Manager
862
902
  * https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel
@@ -941,5 +981,5 @@ declare class BroadcastChannelManager {
941
981
  unsubscribeAll(): void;
942
982
  }
943
983
 
944
- export { ArsCoreModule, ArsDateFnsModule, AutoFocusDirective, BroadcastChannelManager, BroadcastService, CopyClipboardDirective, DateFnsAdapter, DateFormat, DateInterval, DateIntervalChangeDirective, DeleteModel, EmailsValidatorDirective, EnvironmentService, EqualsValidatorDirective, FileInfo, FileSizeValidatorDirective, FormatHtmlPipe, FormatPipe, GroupModel, GuidValidatorDirective, IDModel, ImportModel, MAT_DATE_FNS_FORMATS, MaxTermsValidatorDirective, NotEqualValidatorDirective, NotFutureValidatorDirective, PasswordValidatorDirective, QueryModel, RelationModel, RemoveFocusDirective, ReplacePipe, SafeHtmlPipe, SafeUrlPipe, ScreenService, SearchCallbackPipe, SearchFilterPipe, SelectableModel, SqlDateValidatorDirective, SystemUtils, ThemeService, TimeValidatorDirective, UpdateRelationsModel, UrlValidatorDirective, UtilsMessages, ValidIfDirective, ValidatorDirective, ValueModel };
984
+ export { ArsCoreModule, ArsDateFnsModule, AutoFocusDirective, BroadcastChannelManager, BroadcastService, CopyClipboardDirective, DateFnsAdapter, DateFormat, DateInterval, DateIntervalChangeDirective, DeleteModel, EmailsValidatorDirective, EnvironmentService, EqualsValidatorDirective, FileInfo, FileSizeValidatorDirective, FormatHtmlPipe, FormatMarkdownPipe, FormatPipe, GroupModel, GuidValidatorDirective, IDModel, ImportModel, MAT_DATE_FNS_FORMATS, MaxTermsValidatorDirective, NotEqualValidatorDirective, NotFutureValidatorDirective, PasswordValidatorDirective, QueryModel, RelationModel, RemoveFocusDirective, ReplacePipe, SafeHtmlPipe, SafeUrlPipe, ScreenService, SearchCallbackPipe, SearchFilterPipe, SelectableModel, SqlDateValidatorDirective, SystemUtils, ThemeService, TimeValidatorDirective, UpdateRelationsModel, UrlValidatorDirective, UtilsMessages, ValidIfDirective, ValidatorDirective, ValueModel };
945
985
  export type { AddModel, AddResultModel, ApiResponse, ApiResult, BroadcastChannelMessageBag, BroadcastChannelSubscriberInfo, BroadcastMessageInfo, Checkable, DeleteResultModel, DoneResult, EnableDisableModel, ErrorInfo, File, Folder, FolderTree, INode, LoginResult, NameValueItem, PasswordStrength, QueryResultModel, SearchBag, SearchFilterMetadata, Searchable, SendToModel, ThemeType, UpdateModel, UpdateResultModel, Validated };
@@ -321,89 +321,203 @@ class SystemUtils {
321
321
  return s;
322
322
  }
323
323
  /**
324
- * Convert a markdown string to html
325
- * @param markdown : the markdown string
326
- * @returns
324
+ * Convert markdown to html
325
+ * @param markdown : the markdown data
326
+ * @returns the html
327
327
  */
328
328
  static markdownToHtml(markdown) {
329
- if (!markdown)
330
- return undefined;
331
329
  let html = markdown;
332
- // Headers
330
+ //1. Escape HTML entities
331
+ html = html.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
332
+ // 2. Code blocks
333
+ html = html.replace(/```(\w+)?\n([\s\S]*?)```/g, (_match, lang, code) => {
334
+ const langClass = lang ? ` class="language-${lang}"` : '';
335
+ return `<pre><code${langClass}>${code.trim()}</code></pre>`;
336
+ });
337
+ // 3. Inline code
338
+ html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
339
+ // 4. Headers
340
+ html = html.replace(/^##### (.*$)/gim, '<h5>$1</h5>');
341
+ html = html.replace(/^#### (.*$)/gim, '<h4>$1</h4>');
333
342
  html = html.replace(/^### (.*$)/gim, '<h3>$1</h3>');
334
343
  html = html.replace(/^## (.*$)/gim, '<h2>$1</h2>');
335
344
  html = html.replace(/^# (.*$)/gim, '<h1>$1</h1>');
336
- // Bold
337
- html = html.replace(/\*\*(.*)\*\*/gim, '<strong>$1</strong>');
338
- html = html.replace(/__(.*?)__/gim, '<strong>$1</strong>');
339
- // Italic
340
- html = html.replace(/\*(.*?)\*/gim, '<em>$1</em>');
341
- html = html.replace(/_(.*?)_/gim, '<em>$1</em>');
342
- // Code blocks
343
- html = html.replace(/```([\s\S]*?)```/gim, '<pre><code>$1</code></pre>');
344
- // Inline code
345
- html = html.replace(/`(.*?)`/gim, '<code>$1</code>');
346
- // Links
347
- html = html.replace(/\[([^\]]*)\]\(([^)]*)\)/gim, '<a href="$2">$1</a>');
348
- // Images
349
- html = html.replace(/!\[([^\]]*)\]\(([^)]*)\)/gim, '<img alt="$1" src="$2" />');
350
- // Blockquotes
351
- html = html.replace(/^\> (.*$)/gim, '<blockquote>$1</blockquote>');
352
- // Unordered lists
353
- html = html.replace(/^\* (.*$)/gim, '<li>$1</li>');
354
- html = html.replace(/^- (.*$)/gim, '<li>$1</li>');
355
- // Ordered lists
356
- html = html.replace(/^\d+\. (.*$)/gim, '<li>$1</li>');
357
- // Wrap list items in ul/ol tags
358
- html = html.replace(/(<li>.*<\/li>)/gims, function (match) {
359
- return '<ul>' + match + '</ul>';
360
- });
361
- // Tables
362
- html = html.replace(/\|(.+)\|/g, function (_match, content) {
363
- const cells = content.split('|').map((cell) => cell.trim());
364
- return '<tr>' + cells.map((cell) => {
365
- // Check if this is a header separator row
366
- if (cell.match(/^[\s\-\:]+$/)) {
367
- return null;
345
+ // 5. Horizontal rules
346
+ html = html.replace(/^---+$/gim, '<hr>');
347
+ html = html.replace(/^\*\*\*+$/gim, '<hr>');
348
+ // 6. Links
349
+ html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>');
350
+ // 7. Images
351
+ html = html.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img src="$2" alt="$1">');
352
+ // 8. Bold e Italic
353
+ html = html.replace(/\*\*\*([^*]+)\*\*\*/gim, '<strong><em>$1</em></strong>');
354
+ html = html.replace(/___([^_]+)___/gim, '<strong><em>$1</em></strong>');
355
+ html = html.replace(/\*\*([^*]+)\*\*/gim, '<strong>$1</strong>');
356
+ html = html.replace(/__([^_]+)__/gim, '<strong>$1</strong>');
357
+ html = html.replace(/\*([^*]+)\*/gim, '<em>$1</em>');
358
+ html = html.replace(/_([^_]+)_/gim, '<em>$1</em>');
359
+ // 9. Strikethrough
360
+ html = html.replace(/~~([^~]+)~~/gim, '<del>$1</del>');
361
+ // 10. Blockquotes
362
+ html = html.replace(/^> (.*$)/gim, '<blockquote>$1</blockquote>');
363
+ // 11. Tables
364
+ html = this.markdownConvertTables(html);
365
+ // 12. Lists
366
+ html = this.markdownConvertLists(html);
367
+ // 13. Line breaks
368
+ html = html.replace(/ $/gm, '<br>');
369
+ // 14. Paragrafi (ultimo step per non interferire con altri elementi)
370
+ html = this.markdownConvertParagraphs(html);
371
+ // 15. Clean up extra newlines
372
+ html = html.replace(/\n{3,}/g, '\n\n');
373
+ return html.trim();
374
+ }
375
+ /**
376
+ * Convert markdown table to html
377
+ * @param markdown: the markdown string
378
+ * @returns : the html
379
+ */
380
+ static markdownConvertTables(markdown) {
381
+ const lines = markdown.split('\n');
382
+ const result = [];
383
+ let inTable = false;
384
+ let tableRows = [];
385
+ let headerProcessed = false;
386
+ for (let i = 0; i < lines.length; i++) {
387
+ const line = lines[i].trim();
388
+ if (line.includes('|') && line.length > 1) {
389
+ const nextLine = i + 1 < lines.length ? lines[i + 1].trim() : '';
390
+ const isHeaderSeparator = /^\|?[\s\-:|]+\|?$/.test(nextLine);
391
+ if (!inTable) {
392
+ inTable = true;
393
+ headerProcessed = false;
394
+ tableRows = [];
395
+ }
396
+ if (isHeaderSeparator && !headerProcessed) {
397
+ tableRows.push(this.markdownConvertTableRow(line, true));
398
+ headerProcessed = true;
399
+ i++;
400
+ }
401
+ else if (!isHeaderSeparator) {
402
+ tableRows.push(this.markdownConvertTableRow(line, false));
368
403
  }
369
- return '<td>' + cell + '</td>';
370
- }).filter((cell) => cell !== null).join('') + '</tr>';
371
- });
372
- // Convert table rows to proper table structure
373
- html = html.replace(/(<tr>.*?<\/tr>)\s*\n*(<tr>.*?<\/tr>)/gs, function (match, firstRow, rest) {
374
- // Check if the second row is a separator (contains only dashes, colons, spaces)
375
- const secondRowContent = rest.replace(/<\/?t[rd]>/g, '');
376
- if (secondRowContent.match(/^[\s\-\:\|]+$/)) {
377
- // This is a header separator, make first row a header
378
- const headerRow = firstRow.replace(/<td>/g, '<th>').replace(/<\/td>/g, '</th>');
379
- return headerRow;
380
404
  }
381
- return match;
382
- });
383
- // Wrap table rows in table tags
384
- html = html.replace(/(<tr>.*?<\/tr>(?:\s*<tr>.*?<\/tr>)*)/gs, '<table>$1</table>');
385
- // Horizontal rules
386
- html = html.replace(/^\-\-\-$/gim, '<hr>');
387
- html = html.replace(/^\*\*\*$/gim, '<hr>');
388
- // Line breaks
389
- html = html.replace(/\n\n/gim, '</p><p>');
390
- html = html.replace(/\n/gim, '<br>');
391
- // Wrap in paragraphs
392
- html = '<p>' + html + '</p>';
393
- // Clean up empty paragraphs
394
- html = html.replace(/<p><\/p>/gim, '');
395
- html = html.replace(/<p>(<h[1-6]>)/gim, '$1');
396
- html = html.replace(/(<\/h[1-6]>)<\/p>/gim, '$1');
397
- html = html.replace(/<p>(<ul>)/gim, '$1');
398
- html = html.replace(/(<\/ul>)<\/p>/gim, '$1');
399
- html = html.replace(/<p>(<blockquote>)/gim, '$1');
400
- html = html.replace(/(<\/blockquote>)<\/p>/gim, '$1');
401
- html = html.replace(/<p>(<pre>)/gim, '$1');
402
- html = html.replace(/(<\/pre>)<\/p>/gim, '$1');
403
- html = html.replace(/<p>(<table>)/gim, '$1');
404
- html = html.replace(/(<\/table>)<\/p>/gim, '$1');
405
- html = html.replace(/<p>(<hr>)<\/p>/gim, '$1');
406
- return html;
405
+ else {
406
+ if (inTable && tableRows.length > 0) {
407
+ result.push('<table>');
408
+ result.push(...tableRows);
409
+ result.push('</table>');
410
+ tableRows = [];
411
+ inTable = false;
412
+ }
413
+ result.push(line);
414
+ }
415
+ }
416
+ if (inTable && tableRows.length > 0) {
417
+ result.push('<table>');
418
+ result.push(...tableRows);
419
+ result.push('</table>');
420
+ }
421
+ return result.join('\n');
422
+ }
423
+ /**
424
+ * Convert a markdown table row to html
425
+ * @param row : the markdown row
426
+ * @param isHeader : true if is header
427
+ * @returns the html
428
+ */
429
+ static markdownConvertTableRow(row, isHeader) {
430
+ const cells = row.split('|').map(cell => cell.trim()).filter(cell => cell !== '');
431
+ const tag = isHeader ? 'th' : 'td';
432
+ const cellsHtml = cells.map(cell => ` <${tag}>${cell}</${tag}>`).join('\n');
433
+ return ` <tr>\n${cellsHtml}\n </tr>`;
434
+ }
435
+ /**
436
+ * Convert a markdown list to html
437
+ * @param markdown : the markdown list
438
+ * @returns the html
439
+ */
440
+ static markdownConvertLists(markdown) {
441
+ markdown = markdown.replace(/^\* (.*$)/gim, '<li data-type="ul">$1</li>');
442
+ markdown = markdown.replace(/^- (.*$)/gim, '<li data-type="ul">$1</li>');
443
+ markdown = markdown.replace(/^\+ (.*$)/gim, '<li data-type="ul">$1</li>');
444
+ markdown = markdown.replace(/^\d+\. (.*$)/gim, '<li data-type="ol">$1</li>');
445
+ const lines = markdown.split('\n');
446
+ const result = [];
447
+ let currentList = { type: null, items: [] };
448
+ for (const line of lines) {
449
+ const ulMatch = line.match(/^<li data-type="ul">(.*)<\/li>$/);
450
+ const olMatch = line.match(/^<li data-type="ol">(.*)<\/li>$/);
451
+ if (ulMatch) {
452
+ if (currentList.type !== 'ul') {
453
+ if (currentList.type && currentList.items.length > 0) {
454
+ result.push(this.markdownWrapList(currentList.type, currentList.items));
455
+ }
456
+ currentList = { type: 'ul', items: [ulMatch[1]] };
457
+ }
458
+ else {
459
+ currentList.items.push(ulMatch[1]);
460
+ }
461
+ }
462
+ else if (olMatch) {
463
+ if (currentList.type !== 'ol') {
464
+ if (currentList.type && currentList.items.length > 0) {
465
+ result.push(this.markdownWrapList(currentList.type, currentList.items));
466
+ }
467
+ currentList = { type: 'ol', items: [olMatch[1]] };
468
+ }
469
+ else {
470
+ currentList.items.push(olMatch[1]);
471
+ }
472
+ }
473
+ else {
474
+ if (currentList.type && currentList.items.length > 0) {
475
+ result.push(this.markdownWrapList(currentList.type, currentList.items));
476
+ currentList = { type: null, items: [] };
477
+ }
478
+ result.push(line);
479
+ }
480
+ }
481
+ if (currentList.type && currentList.items.length > 0) {
482
+ result.push(this.markdownWrapList(currentList.type, currentList.items));
483
+ }
484
+ return result.join('\n');
485
+ }
486
+ /**
487
+ * Wrap a list
488
+ * @param type: the list type
489
+ * @param items : the item list
490
+ * @returns the html
491
+ */
492
+ static markdownWrapList(type, items) {
493
+ const listItems = items.map(item => ` <li>${item}</li>`).join('\n');
494
+ return `<${type}>\n${listItems}\n</${type}>`;
495
+ }
496
+ /**
497
+ * Convert markdown paragraphs to html
498
+ * @param markdown : the markdown paragraphs
499
+ * @returns the html
500
+ */
501
+ static markdownConvertParagraphs(markdown) {
502
+ const lines = markdown.split('\n');
503
+ const result = [];
504
+ for (const line of lines) {
505
+ const trimmed = line.trim();
506
+ if (!trimmed) {
507
+ result.push('');
508
+ continue;
509
+ }
510
+ if (trimmed.startsWith('<') && trimmed.endsWith('>')) {
511
+ result.push(line);
512
+ continue;
513
+ }
514
+ if (trimmed.startsWith('<') || trimmed.endsWith('>')) {
515
+ result.push(line);
516
+ continue;
517
+ }
518
+ result.push(`<p>${trimmed}</p>`);
519
+ }
520
+ return result.join('\n');
407
521
  }
408
522
  /**
409
523
  * Compare two names
@@ -2530,6 +2644,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.5", ngImpor
2530
2644
  args: ['click', ['$event']]
2531
2645
  }] } });
2532
2646
 
2647
+ class FormatMarkdownPipe {
2648
+ constructor() {
2649
+ this.sanitizer = inject(DomSanitizer);
2650
+ }
2651
+ transform(value) {
2652
+ return this.sanitizer.bypassSecurityTrustHtml(SystemUtils.markdownToHtml(value ?? ''));
2653
+ }
2654
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: FormatMarkdownPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
2655
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.0.5", ngImport: i0, type: FormatMarkdownPipe, isStandalone: true, name: "formatMarkdown" }); }
2656
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: FormatMarkdownPipe }); }
2657
+ }
2658
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: FormatMarkdownPipe, decorators: [{
2659
+ type: Injectable
2660
+ }, {
2661
+ type: Pipe,
2662
+ args: [{
2663
+ name: 'formatMarkdown',
2664
+ standalone: true
2665
+ }]
2666
+ }] });
2667
+
2533
2668
  /*
2534
2669
  * Public API Surface of ars-utils
2535
2670
  */
@@ -2538,5 +2673,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.5", ngImpor
2538
2673
  * Generated bundle index. Do not edit.
2539
2674
  */
2540
2675
 
2541
- export { ArsCoreModule, ArsDateFnsModule, AutoFocusDirective, BroadcastChannelManager, BroadcastService, CopyClipboardDirective, DateFnsAdapter, DateFormat, DateInterval, DateIntervalChangeDirective, DeleteModel, EmailsValidatorDirective, EnvironmentService, EqualsValidatorDirective, FileInfo, FileSizeValidatorDirective, FormatHtmlPipe, FormatPipe, GroupModel, GuidValidatorDirective, IDModel, ImportModel, MAT_DATE_FNS_FORMATS, MaxTermsValidatorDirective, NotEqualValidatorDirective, NotFutureValidatorDirective, PasswordValidatorDirective, QueryModel, RelationModel, RemoveFocusDirective, ReplacePipe, SafeHtmlPipe, SafeUrlPipe, ScreenService, SearchCallbackPipe, SearchFilterPipe, SelectableModel, SqlDateValidatorDirective, SystemUtils, ThemeService, TimeValidatorDirective, UpdateRelationsModel, UrlValidatorDirective, UtilsMessages, ValidIfDirective, ValidatorDirective, ValueModel };
2676
+ export { ArsCoreModule, ArsDateFnsModule, AutoFocusDirective, BroadcastChannelManager, BroadcastService, CopyClipboardDirective, DateFnsAdapter, DateFormat, DateInterval, DateIntervalChangeDirective, DeleteModel, EmailsValidatorDirective, EnvironmentService, EqualsValidatorDirective, FileInfo, FileSizeValidatorDirective, FormatHtmlPipe, FormatMarkdownPipe, FormatPipe, GroupModel, GuidValidatorDirective, IDModel, ImportModel, MAT_DATE_FNS_FORMATS, MaxTermsValidatorDirective, NotEqualValidatorDirective, NotFutureValidatorDirective, PasswordValidatorDirective, QueryModel, RelationModel, RemoveFocusDirective, ReplacePipe, SafeHtmlPipe, SafeUrlPipe, ScreenService, SearchCallbackPipe, SearchFilterPipe, SelectableModel, SqlDateValidatorDirective, SystemUtils, ThemeService, TimeValidatorDirective, UpdateRelationsModel, UrlValidatorDirective, UtilsMessages, ValidIfDirective, ValidatorDirective, ValueModel };
2542
2677
  //# sourceMappingURL=arsedizioni-ars-utils-core.mjs.map